【原型設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現

2023-04-24 12:01:09

簡介

原型模式(Prototype Pattern)是一種建立型設計模式,使你能夠複製已有物件,而無需使程式碼依賴它們所屬的類,同時又能保證效能。

這種模式是實現了一個原型介面,該介面用於建立當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。

如果你需要複製一些物件,同時又希望程式碼獨立於這些物件所屬的具體類,可以使用原型模式。

作用

  1. 利用已有的一個原型物件,快速地生成和原型物件一樣的範例。
  2. 跳過建構函式的約束,便於提升效能。

實現步驟

  1. 建立原型介面,並宣告克隆方法。
  2. 使用new運運算元呼叫原型版本的建構函式。
  3. 將子類建構函式的直接呼叫,替換為對原型工廠方法的呼叫。

UML

 

Java程式碼

基礎原型抽象類

// Shape.java 基礎抽象類
public abstract class Shape implements Cloneable {

  private int width;
  private int height;
  private String color = "";
  protected String type;

  public Shape() {

  }

  public String getType() {
    return type;
  }

  // 抽象方法,子類覆蓋
  public abstract void draw();

  public void setWidth(int width) {
    this.width = width;
  }

  public int getWidth() {
    return this.width;
  }

  public int getHeight() {
    return this.height;
  }

  public void setHeight(int height) {
    this.height = height;
  }

  public void setColor(String color) {
    this.color = color;
  }

  public String getColor() {
    return this.color;
  }

  // 克隆方法
  public Object clone() {
    Object clone = null;
    try {
      clone = super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return clone;
  }

  @Override
  public String toString() {
    return String.format("{width = %s, height = %s, type = %s, color = %s }",
        this.width, this.height, this.type, this.color);
  }
}

具體原型者

// Circle.java 具體原型類,克隆方法會建立一個新物件並將其傳遞給建構函式。
public class Circle extends Shape {
  public Circle() {
    super();
    type = "Circle";
  }

  @Override
  public void draw() {
    System.out.println("Circle::draw() method.");
  }
}
// Rectangle.java 具體原型類,克隆方法會建立一個新物件並將其傳遞給建構函式。
public class Rectangle extends Shape {
  public Rectangle() {
    super();
    type = "Rectangle";
  }

  @Override
  public void draw() {
     System.out.println("Rectangle::draw() method.");
  }
}
// 具體原型類,克隆方法會建立一個新物件並將其傳遞給建構函式。
public class Square extends Shape {
  public Square() {
    super();
    type = "Square";
  }

  @Override
  public void draw() {
    System.out.println("Square::draw() method.");
  }
}

客戶使用類

// Application.java 客戶呼叫方
public class Application {

  public List<Shape> shapes = new ArrayList<Shape>();

  public Application() {
  }

  public void addToShapes() {
    Circle circle = new Circle();
    circle.setWidth(10);
    circle.setHeight(20);
    circle.setColor("red");
    shapes.add(circle);

    // 新增clone
    Circle anotherCircle = (Circle) circle.clone();
    anotherCircle.setColor("pink");
    shapes.add(anotherCircle);
    // 變數 `anotherCircle(另一個圓)`與 `circle(圓)`物件的內容完全一樣。

    Rectangle rectangle = new Rectangle();
    rectangle.setWidth(99);
    rectangle.setHeight(69);
    rectangle.setColor("green");
    shapes.add(rectangle);
    // 新增clone
    shapes.add((Shape) rectangle.clone());
  }

  // 直接取出
  public Shape getShape(Integer index) {
    return this.shapes.get(index);
  }

  // 取出時候clone
  public Shape getShapeClone(Integer index) {
    Shape shape = this.shapes.get(index);
    return (Shape) shape.clone();
  }

  public void printShapes() {
    for (int i = 0; i < this.shapes.size(); i++) {
      Shape shape = this.shapes.get(i);
      System.out.println("shape " + i + " : " + shape.toString());
    }
  }

}

測試呼叫

    /**
     * 原型模式主要就是複製已有的物件,而無需範例化類,從而提升範例化物件時的效能
     * 其實就是複製範例的屬性到新物件上,減少了執行構造的步驟
     */
    Application application = new Application();
    application.addToShapes();
    Shape shapeClone = application.getShapeClone(1);
    // 更改clone
    shapeClone.setColor("gray");
    System.out.println("shapeClone : " + shapeClone.toString());
    // 直接更改
    application.getShape(3).setColor("yellow");
    application.printShapes();

    // /*********************** 分割線 ******************************************/
    application.shapes.add(new Square());
    for (Shape shape : application.shapes) {
      shape.draw();
      System.out.println(shape.toString());
    }

C程式碼

基礎原型抽象類

// func.h 基礎標頭檔案
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

typedef struct Shape shape;
typedef struct Circle circle;
typedef struct Rectangle rectangle;
typedef struct Square square;

// 定義了Shape作為基礎介面,以便各形狀有統一型別
typedef struct Shape
{
    char name[50];
    int width;
    int height;
    char color[50];
    char category[50];
    void (*draw)(struct Shape *shape);
    struct Shape *(*clone)(struct Shape *shape);
    char *(*to_string)(struct Shape *shape);
    void (*set_width)(struct Shape *shape, int width);
    int (*get_width)(struct Shape *shape);
    void (*set_height)(struct Shape *shape, int height);
    int (*get_height)(struct Shape *shape);
    void (*set_color)(struct Shape *shape, char *color);
    char *(*get_color)(struct Shape *shape);
    void (*set_category)(struct Shape *shape, char *category);
    char *(*get_category)(struct Shape *shape);
} Shape;
Shape *shape_constructor(char *name);

typedef struct Circle
{
    char name[50];
    int width;
    int height;
    char color[50];
    char category[50];
    void (*draw)(struct Circle *shape);
    struct Circle *(*clone)(struct Circle *shape);
    char *(*to_string)(struct Circle *shape);
    void (*set_width)(struct Circle *shape, int width);
    int (*get_width)(struct Circle *shape);
    void (*set_height)(struct Circle *shape, int height);
    int (*get_height)(struct Circle *shape);
    void (*set_color)(struct Circle *shape, char *color);
    char *(*get_color)(struct Circle *shape);
    void (*set_category)(struct Circle *shape, char *category);
    char *(*get_category)(struct Circle *shape);
} Circle;
Circle *circle_constructor(char *name);

typedef struct Square
{
    char name[50];
    int width;
    int height;
    char color[50];
    char category[50];
    void (*draw)(struct Square *shape);
    struct Square *(*clone)(struct Square *shape);
    char *(*to_string)(struct Square *shape);
    void (*set_width)(struct Square *shape, int width);
    int (*get_width)(struct Square *shape);
    void (*set_height)(struct Square *shape, int height);
    int (*get_height)(struct Square *shape);
    void (*set_color)(struct Square *shape, char *color);
    char *(*get_color)(struct Square *shape);
    void (*set_category)(struct Square *shape, char *category);
    char *(*get_category)(struct Square *shape);
} Square;
Square *square_constructor(char *name);

typedef struct Rectangle
{
    char name[50];
    int width;
    int height;
    char color[50];
    char category[50];
    void (*draw)(struct Rectangle *shape);
    struct Rectangle *(*clone)(struct Rectangle *shape);
    char *(*string)(struct Rectangle *shape);
    void (*set_width)(struct Rectangle *shape, int width);
    int *(*get_width)(struct Rectangle *shape);
    void (*set_height)(struct Rectangle *shape, int height);
    int *(*get_height)(struct Rectangle *shape);
    void (*set_color)(struct Rectangle *shape, char *color);
    char *(*get_color)(struct Rectangle *shape);
    void (*set_category)(struct Rectangle *shape, char *category);
    char *(*get_category)(struct Rectangle *shape);
} Rectangle;
Rectangle *rectangle_constructor(char *name);

// 呼叫使用者端
typedef struct Application
{
    struct Shape **shapes;
    int shapes_length;
    void (*add_to_shapes)(struct Application *app);
    void (*add_shape)(struct Application *app, Shape *shape);
    Shape *(*get_shape)(struct Application *app, int index);
    Shape **(*get_shapes)(struct Application *app);
    Shape *(*get_shape_clone)(struct Application *app, int index);
    void (*print_shapes)(struct Application *app);
} Application;
Application *application_constructor();
// shape.c 基礎類,供各種具體形狀複用
#include "func.h"

// shape基礎抽象類,供子類繼承覆蓋
// C沒有抽象和繼承,此處作為公共類存在
void shape_draw(Shape *shape)
{
  printf("\r\n Shape::draw()");
}

void shape_set_width(Shape *shape, int width)
{
  shape->width = width;
}

int shape_get_width(Shape *shape)
{
  return shape->width;
}

int shape_get_height(Shape *shape)
{
  return shape->height;
}

void shape_set_height(Shape *shape, int height)
{
  shape->height = height;
}

void shape_set_color(Shape *shape, char *color)
{
  strncpy(shape->color, color, 50);
}

char *shape_get_color(Shape *shape)
{
  return shape->color;
}

void shape_set_category(Shape *shape, char *category)
{
  strncpy(shape->category, category, 50);
}

char *shape_get_category(Shape *shape)
{
  return shape->category;
}

char *shape_to_string(Shape *shape)
{
  static char result[1024];
  sprintf(result, "[name = %s width = %d, height = %d, category = %s, color = %s]",
          shape->name, shape->width, shape->height, shape->category, shape->color);
  return result;
}

// 將指標指向同一記憶體的方式來實現clone
Shape *shape_clone(Shape *shape)
{
  Shape *copy = (Shape *)malloc(sizeof(Shape));
  memcpy(copy, shape, sizeof(Shape));
  strcat(copy->name, "(clone)");
  // printf("\r\n shape_clone: %s", copy->to_string(copy));
  return copy;
}

// 定義簡單結構體,複製基本屬性和draw函數
Shape *shape_clone2(Shape *shape)
{
  struct Shape copy = {
      .width = shape->width,
      .height = shape->height,
  };
  strcpy(copy.name, shape->name);
  strcat(copy.name, "[clone]");
  strcpy(copy.color, shape->color);
  strcpy(copy.category, shape->category);
  Shape *shape_copy = &copy;
  shape_copy->draw = shape->draw;
  // printf("\r\n shape_clone: %s", shape->to_string(shape_copy));
  return shape_copy;
}

Shape *shape_constructor(char *name)
{
  printf("\r\n shape_constructor() [構建Shape]");
  Shape *shape = (Shape *)malloc(sizeof(Shape));
  strncpy(shape->name, name, 50);
  shape->draw = &shape_draw;
  shape->clone = &shape_clone;
  shape->to_string = &shape_to_string;
  shape->set_width = &shape_set_width;
  shape->get_width = &shape_get_width;
  shape->set_height = &shape_set_height;
  shape->get_height = &shape_get_height;
  shape->set_color = &shape_set_color;
  shape->get_color = &shape_get_color;
  shape->set_category = &shape_set_category;
  shape->get_category = &shape_get_category;
  return shape;
}

具體原型者

// circle.c 具體原型類,複用父類別方法,實現自己的draw函數。
#include "func.h"

// 重新定義draw函數
void circle_draw(Circle *shape)
{
  printf("\r\n Circle::draw()");
}

Circle *circle_constructor(char *name)
{
  printf("\r\n shape_constructor() [構建Circle]");
  Shape *shape = (Shape *)shape_constructor(name);
  Circle *circle = (Circle *)shape;
  circle->draw = &circle_draw;
  return circle;
}
// rectangle.c 具體原型類,複用父類別方法,實現自己的draw函數。
#include "func.h"

// 重新定義draw函數
void rectangle_draw(Rectangle *shape)
{
  printf("\r\n Rectangle::draw()");
}

Rectangle *rectangle_constructor(char *name)
{
  printf("\r\n shape_constructor() [構建Rectangle]");
  Shape *shape = (Shape *)shape_constructor(name);
  Rectangle *rectangle = (Rectangle *)shape;
  rectangle->draw = &rectangle_draw;
  return rectangle;
}
// square.c 具體原型類,複用父類別方法,實現自己的draw函數。
#include "func.h"

// 重新定義draw函數
void square_draw(Square *shape)
{
  printf("\r\n Square::draw()");
}

Square *square_constructor(char *name)
{
  printf("\r\n shape_constructor() [構建Square]");
  Shape *shape = (Shape *)shape_constructor(name);
  Square *square = (Square *)shape;
  square->draw = &square_draw;
  return square;
}

客戶使用類

// application.c 客戶呼叫方
#include "func.h"

void app_add_to_shapes(Application *app)
{
  Circle *circle = circle_constructor("circle");
  circle->set_category(circle, "Circle");
  circle->set_width(circle, 10);
  circle->set_height(circle, 20);
  circle->set_color(circle, "red");
  app->add_shape(app, (Shape *)circle);

  // 新增Clone
  Circle *another_circle = circle->clone(circle);
  another_circle->set_color(another_circle, "pink");
  app->add_shape(app, (Shape *)another_circle);
  // 變數 `another_circle(另一個圓)`與 `circle(圓)`物件的內容完全一樣。

  Rectangle *rectangle = rectangle_constructor("rectangle");
  rectangle->set_category(rectangle, "Rectangle");
  rectangle->set_width(rectangle, 99);
  rectangle->set_height(rectangle, 69);
  rectangle->set_color(rectangle, "green");
  app->add_shape(app, (Shape *)rectangle);
  // 再新增一個clone
  app->add_shape(app, (Shape *)rectangle->clone(rectangle));
}

void app_add_shape(Application *app, Shape *shape)
{
  app->shapes_length += 1;
  Shape **new_shapes = (Shape **)calloc(app->shapes_length, sizeof(Shape));
  // 複製原有陣列,並追加新內容到新陣列
  for (int i = 0; i < app->shapes_length - 1; i++)
  {
    new_shapes[i] = app->shapes[i];
  }
  new_shapes[app->shapes_length - 1] = shape;
  free(app->shapes);
  // 指向新陣列
  app->shapes = new_shapes;
}

Shape *app_get_shape(Application *app, int index)
{
  return app->shapes[index];
}

Shape **app_get_shapes(Application *app)
{
  return app->shapes;
}

Shape *app_get_shape_clone(Application *app, int index)
{
  Shape *shape = app->shapes[index];
  if (shape != NULL)
  {
    return shape->clone(shape);
  }
  return NULL;
}

void app_print_shapes(Application *app)
{
  for (int i = 0; i < app->shapes_length; i++)
  {
    Shape *shape = app->shapes[i];
    printf("\r\n shape%d: %s", i, shape->to_string(shape));
  }
}

// 給觀察者繫結主題,同時把觀察者新增到主題列表
Application *application_constructor()
{
  printf("\r\n application_constructor() [構建Application]");
  Application *app = (Application *)malloc(sizeof(Application));
  app->shapes_length = 0;
  app->shapes = (Shape **)calloc(app->shapes_length, sizeof(Shape));
  app->add_to_shapes = &app_add_to_shapes;
  app->add_shape = &app_add_shape;
  app->get_shape = &app_get_shape;
  app->get_shapes = &app_get_shapes;
  app->get_shape_clone = &app_get_shape_clone;
  app->print_shapes = &app_print_shapes;
  return app;
}

測試呼叫

#include "../src/func.h"

int main(void)
{
  printf("test start:\r\n");
  /**
   * 原型模式主要就是複製已有的物件,而無需範例化類,從而提升範例化物件時的效能
   * 其實就是複製範例的屬性到新物件上,減少了執行構造的步驟。
   */
  Application *application = application_constructor();
  application->add_to_shapes(application);
  Shape *shape_clone = application->get_shape_clone(application, 0);
  // SetColor需要介面中定義
  shape_clone->set_color(shape_clone, "gray");
  printf("\r\n shape_clone : %s", shape_clone->to_string(shape_clone));
  // 直接更改
  // application->get_shape(application, 3)->set_color(application->get_shape(application, 3), "yellow");
  application->print_shapes(application);

  /*********************** 分割線 ******************************************/
  // 追加一個Squre範例,相關屬性為空
  application->add_shape(application, (Shape *)square_constructor("square"));
  // 打不列印檢視結果

  for (int i = 0; i < application->shapes_length; i++)
  {
    Shape *shape = application->shapes[i];
    shape->draw(shape);
    printf("\r\n shape_%d %s", i, shape->to_string(shape));
  }
}

更多語言版本

不同語言實現設計模式:https://github.com/microwind/design-pattern