菜鸟教程 -- 学的不仅是技术,更是梦想!

设计模式
(追記) (追記ここまで)

原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

概要

原型模式

意图:使用原型实例指定要创建对象的种类,并通过拷贝这些原型创建新的对象。

主要解决:在运行时动态建立和删除原型。

何时使用

  • 系统应独立于产品的创建、构成和表示。
  • 需要在运行时指定实例化的类,例如通过动态加载。
  • 避免创建与产品类层次平行的工厂类层次。
  • 类的实例只能有几种不同状态组合,克隆原型比手工实例化更方便。

如何解决

通过已有的一个原型对象,快速生成与原型对象相同的实例。

关键代码

  • 实现克隆操作:
    • 在 Java 中,实现 Cloneable 接口,重写 clone() 方法。
    • 在 .NET 中,使用 Object 类的 MemberwiseClone() 方法实现浅拷贝,或通过序列化实现深拷贝。
  • 隔离类对象的使用者和具体类型之间的耦合关系,要求"易变类"拥有稳定的接口。

应用实例

  • 细胞分裂
  • Java 中的 Object.clone() 方法

优点

  • 性能提高
  • 避免构造函数的约束

缺点

  • 配备克隆方法需要全面考虑类的功能,对已有类可能较难实现,特别是处理不支持串行化的间接对象或含有循环结构的引用时。
  • 必须实现 Cloneable 接口。

使用场景

  • 资源优化
  • 类初始化需要消耗大量资源(如数据、硬件资源)
  • 性能和安全要求高的场景
  • 通过 new 创建对象需要复杂的数据准备或访问权限时
  • 一个对象需要多个修改者
  • 对象需提供给其他对象访问并可能被各个调用者修改时
  • 通常与工厂方法模式一起使用,通过 clone 创建对象,然后由工厂方法提供给调用者

注意事项

与直接实例化类创建新对象不同,原型模式通过拷贝现有对象生成新对象。浅拷贝通过实现 Cloneable 实现,深拷贝通过实现 Serializable 读取二进制流实现。

结构

原型模式包含以下几个主要角色:

  • 原型接口(Prototype Interface):定义一个用于克隆自身的接口,通常包括一个 clone() 方法。

  • 具体原型类(Concrete Prototype):实现原型接口的具体类,负责实际的克隆操作。这个类需要实现 clone() 方法,通常使用浅拷贝或深拷贝来复制自身。

  • 客户端(Client):使用原型实例来创建新的对象。客户端调用原型对象的 clone() 方法来创建新的对象,而不是直接使用构造函数。

实现

我们将创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache,该类把 shape 对象存储在一个 Hashtable 中,并在请求的时候返回它们的克隆。

PrototypePatternDemo 类使用 ShapeCache 类来获取 Shape 对象。

原型模式的 UML 图

步骤 1

创建一个实现了 Cloneable 接口的抽象类。

Shape.java

publicabstractclassShapeimplementsCloneable{privateStringid; protectedStringtype; abstractvoiddraw(); publicStringgetType(){returntype; }publicStringgetId(){returnid; }publicvoidsetId(Stringid){this.id = id; }publicObjectclone(){Objectclone = null; try{clone = super.clone(); }catch(CloneNotSupportedExceptione){e.printStackTrace(); }returnclone; }}

步骤 2

创建扩展了上面抽象类的实体类。

Rectangle.java

publicclassRectangleextendsShape{publicRectangle(){type = "Rectangle"; } @Overridepublicvoiddraw(){System.out.println("Inside Rectangle::draw() method."); }}

Square.java

publicclassSquareextendsShape{publicSquare(){type = "Square"; } @Overridepublicvoiddraw(){System.out.println("Inside Square::draw() method."); }}

Circle.java

publicclassCircleextendsShape{publicCircle(){type = "Circle"; } @Overridepublicvoiddraw(){System.out.println("Inside Circle::draw() method."); }}

步骤 3

创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。

ShapeCache.java

importjava.util.Hashtable; publicclassShapeCache{privatestaticHashtable<String, Shape> shapeMap = newHashtable<String, Shape>(); publicstaticShapegetShape(StringshapeId){ShapecachedShape = shapeMap.get(shapeId); return(Shape)cachedShape.clone(); }// 对每种形状都运行数据库查询,并创建该形状// shapeMap.put(shapeKey, shape);// 例如,我们要添加三种形状publicstaticvoidloadCache(){Circlecircle = newCircle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Squaresquare = newSquare(); square.setId("2"); shapeMap.put(square.getId(),square); Rectanglerectangle = newRectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); }}

步骤 4

PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。

PrototypePatternDemo.java

publicclassPrototypePatternDemo{publicstaticvoidmain(String[]args){ShapeCache.loadCache(); ShapeclonedShape = (Shape)ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); ShapeclonedShape2 = (Shape)ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); ShapeclonedShape3 = (Shape)ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); }}

步骤 5

执行程序,输出结果:

Shape : Circle
Shape : Square
Shape : Rectangle
AI 思考中...

4 篇笔记 写笔记

  1. #0

    jade

    guo***[email protected]

    47

    原型模式中有三个登场角色:

    原型角色:定义用于复制现有实例来生成新实例的方法;

    // 以贴主示例代码为例 
    implements Cloneable // 1.(抽象类或者接口)实现 java.lang.Cloneable 接口
    public Shape clone(); // 2.定义复制现有实例来生成新实例的方法

    具体原型角色:实现用于复制现有实例来生成新实例的方法

    public Shape clone() {// 2.实现复制现有实例来生成新实例的方法(也可以由超类完成)
     Shape clone = null;
     try {
     clone = (Shape) clone();
     } catch (CloneNotSupportedException e) {
     e.printStackTrace();
     }
     return clone;
    }

    使用者角色:维护一个注册表,并提供一个找出正确实例原型的方法。最后,提供一个获取新实例的方法,用来委托复制实例的方法生成新实例。

    private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();//维护一个注册表
     public static void loadCache() {
     Circle circle = new Circle();
     circle.setId("1");
     shapeMap.put(circle.getId(),circle);
     Square square = new Square();
     square.setId("2");
     shapeMap.put(square.getId(),square);
     Rectangle rectangle = new Rectangle();
     rectangle.setId("3");
     shapeMap.put(rectangle.getId(),rectangle);
    }
    public static Shape getShape(String shapeId) {//提供一个获取新实例的方法
     Shape cachedShape = shapeMap.get(shapeId);//提供一个找出正确实例原型的方法
     return (Shape) cachedShape.clone();//委托复制实例的方法生成新实例。
    }

    jade

    guo***[email protected]

    8年前 (2018年09月20日)
  2. #0

    Siskin.xu

    sis***@sohu.com

    13

    Python 方式:

    # 原型模式 with Python
    import copy
    # 用copy包实现深拷贝(copy.deepcopy())和浅拷贝(copy.copy())
    from abc import abstractmethod,ABCMeta
    # 创建一接口
    class Shape(metaclass=ABCMeta):
     _id =""
     in_type = ""
     @abstractmethod
     def draw(self):
     pass
     def getType(self):
     return self.in_type
     def getID(self):
     return self._id
     def setID(self,in_id):
     self._id = in_id
     def clone(self):
     #深拷贝
     myclone = copy.deepcopy(self)
     return myclone
    # 创建实体类
    class Rectangle(Shape):
     def __init__(self):
     self.in_type = "Rectangel"
     def draw(self):
     print("Inside Rectangle.draw() method.")
    class Square(Shape):
     def __init__(self):
     self.in_type = "Square"
     def draw(self):
     print("Inside Square.draw() method.")
    class Circle(Shape):
     def __init__(self):
     self.in_type = "Circle"
     def draw(self):
     print("Inside Circle.draw() method.")
    # 获取数据实体类
    class ShapeCache():
     #Python 无静态变量,用开放类变量
     shapeMap = {}
     def getShape(self,shapeID):
     cachedShape = self.shapeMap[shapeID]
     return cachedShape.clone()
     # 静态方法
     @staticmethod
     def loadCache():
     circle1 = Circle()
     circle1.setID("1")
     ShapeCache.shapeMap[circle1.getID()] = circle1
     square1 = Square()
     square1.setID("2")
     ShapeCache.shapeMap[square1.getID()] = square1
     rectangle1 = Rectangle()
     rectangle1.setID("3")
     ShapeCache.shapeMap[rectangle1.getID()] = rectangle1
    # 调用输出
    if __name__ == '__main__':
     ShapeCache.loadCache()
     myShape = ShapeCache()
     cloneShape1 = myShape.getShape("1")
     print("Shape : %s" %cloneShape1.getType())
     cloneShape1.draw()
     cloneShape2 = myShape.getShape("2")
     print("Shape : %s" % cloneShape2.getType())
     cloneShape2.draw()
     cloneShape3 = myShape.getShape("3")
     print("Shape : %s" % cloneShape3.getType())
     cloneShape3.draw()

    Siskin.xu

    sis***@sohu.com

    6年前 (2020年02月28日)
  3. #0

    kemplaw

    kem***[email protected]

    5

    TypeScript 方式:

    class Author {
     private pages: Page[] = []
     constructor(private name: string) {}
     addToPage(page: Page): void {
     this.pages.push(page)
     }
    }
    class Page {
     private commonts: string[] = []
     private date: Date
     constructor(private title: string, private body: string, private author: Author) {
     author.addToPage(this)
     this.date = new Date()
     }
     addComment(comment: string): void {
     this.commonts.push(comment)
     }
     clone(): this {
     const clone: this = Object.create(this)
     clone.title = `Copy of ${this.title}`
     clone.author.addToPage(clone)
     clone.commonts = []
     clone.date = new Date()
     return clone
     }
    }
    function prototypeTest() {
     const author = new Author('作者A')
     const page = new Page('标题', '内容', author)
     page.addComment('这是一段评论')
     const copyPage = page.clone()
     console.log(copyPage)
     console.log(author)
     console.log(copyPage === page)
    }
    prototypeTest()

    kemplaw

    kem***[email protected]

    6年前 (2020年06月15日)
  4. #0

    DF3453

    429***[email protected]

    1

    原型模式是一种灵活而强大的创建型设计模式,适用于需要通过克隆操作创建新对象的场景。

    下面是一个示例代码,演示了如何使用原型模式:

    // 原型接口
    interface Prototype {
     Prototype clone();
    }
    // 具体原型类
    class ConcretePrototype implements Prototype {
     private int property;
     public ConcretePrototype(int property) {
     this.property = property;
     }
     public void setProperty(int property) {
     this.property = property;
     }
     public int getProperty() {
     return property;
     }
     @Override
     public Prototype clone() {
     // 执行深克隆或浅克隆操作,返回新的对象
     return new ConcretePrototype(property);
     }
    }
    // 客户端使用原型模式创建对象
    public class Client {
     public static void main(String[] args) {
     // 创建原型对象
     Prototype prototype = new ConcretePrototype(10);
     // 克隆新对象
     Prototype clone = prototype.clone();
     System.out.println(clone.getProperty()); // 输出:10
     // 修改克隆对象的属性
     clone.setProperty(20);
     System.out.println(clone.getProperty()); // 输出:20
     }
    }

    在上面的示例中,原型接口 Prototype 定义了 clone() 方法,具体原型类 ConcretePrototype 实现了该接口,并提供了深克隆或浅克隆的具体实现。客户端代码通过调用 clone() 方法来创建新的对象,并可以根据需求修改新对象的属性。

    原型模式的优点包括:

    • 简化对象的创建过程,避免了重复的初始化代码。
    • 可以动态添加或删除原型,根据需要创建新的对象。
    • 提供了一种简单的方式来实现对象的多态性。

    原型模式也有一些注意事项:

    • 对于包含循环引用或复杂对象关联关系的对象,深克隆可能会变得复杂。
    • 克隆操作可能会影响性能,特别是当对象包含大量数据时。

    DF3453

    429***[email protected]

    3年前 (2023年07月11日)

点我分享笔记

  • 昵称 (必填)
  • 邮箱 (必填)
  • 引用地址

AltStyle によって変換されたページ (->オリジナル) /