创意设计模式:原型设计模式

发布时间:2024年01月10日

概述

原型设计模式是创建性设计模式之一。它允许我们指定作为原始对象原型的对象。换句话说,它允许我们复制现有对象而不是创建新实例。

在这里,我们将详细了解原型设计模式及其用法。

我们什么时候需要原型设计模式?

原型设计模式主要用于从头开始创建新对象的成本很高,并且我们已经有类似的类来创建此类对象。

原型设计模式的帮助下,我们从现有的类中复制新对象,并根据我们的新要求修改这些对象。

真实世界的例子

假设我们有一个程序用于将数据从一个特定来源插入数据库

现在,我们收到了一个新要求,在将数据插入数据库之前,我们需要执行某种类型的数据预处理和数据修改。由于我们已经有一个用于插入数据的程序,因此我们可以使用此对象并对其进行一些更改,以便它可以修改/预处理数据,然后将其插入数据库。

为此,我们将现有对象克隆到新对象中,并对这个新对象进行必要的更改。

对于原型设计模式,必须在现有对象中具有复制功能。如果我们想创建对象的原型,我们需要为该对象/类实现/定义一个?clone()?方法。虽然,这取决于我们的设计和要求,是使用现有对象的浅拷贝还是深拷贝。

浅拷贝 - 在浅拷贝中,我们只克隆父对象,而不克隆其包含对象。

  • 在浅拷贝中,我们复制原始对象的引用,因此,如果我们对其中一个对象进行任何更改,这些更改也会反映在其他对象中

深层复制?- 在深层复制中,我们克隆父对象及其包含对象。

  • 在深层复制中,我们复制原始对象及其值,因此在复制后,两个对象彼此独立,因此,如果我们在一个对象中进行任何更改,该更改不会反映在另一个对象中

其他示例列表

  1. 手机制造公司为各个细分市场发布不同的手机。现在,每个手机都将具有一些基本的通用功能,但有一定程度的变化。在这里,该公司将使用其手机软件之一,并根据其他手机进行克隆。

  2. 笔记本电脑制造公司在软件中为一台笔记本电脑设计了笔记本电脑的机身。之后,对于这款笔记本电脑型号的未来版本,该公司将简单地克隆上一个型号的机身,并根据最新趋势和要求进行必要的更改。

在这里,我们了解了原型设计模式在实际场景中的工作原理。此外,我们将看到它将如何工作及其内部实现

原型设计模式如何工作?

原型设计模式的结构

  • 从上图中我们可以了解到,clone()?方法是在?Prototype 接口中声明的。在大多数情况下,只有一个?clone()?方法。
  • 此?clone() 方法在?Concrete Prototype?类(以绿色表示)中实现。在这里,clone()?方法将数据从原始对象复制到克隆对象中。此外,此处还管理与复制对象相关的边界值条件。
  • 客户端(以蓝色表示)可以在不实现底层代码的情况下创建原始对象的副本。

让我们了解原型设计模式的详细实现。

首先,原型允许我们从客户端代码中隐藏创建新对象实例的复杂性。这背后的基本概念是复制现有对象,而不是从头开始创建新的对象实例,该实例可能具有一些复杂的操作或业务逻辑。简单来说,通过使用这种设计模式,最终用户不需要担心原始对象的内部实现,也不需要检查原始对象的每个属性并将其分配给复制的对象。

因此,现有对象表示为 Prototype 接口并包含一个基对象。如果需要,新创建的子类可以包含外部元素和方法。如果我们想改变它的功能,我们可以向原型对象添加更多的功能/类

当对象开发是一项耗时且成本高昂的操作时,这种原型模式是必要的,因此我们可以使用现有对象实例本身创建对象。

在?Java 和 Python?中,内置的 Clone()?方法是从现有对象创建对象的最佳可用选项。这是实现原型模式的最简单方法。但是,原型的实现可能会根据业务需求和语言而变化(例如,在 C++ 中,我们没有任何内置方法来克隆对象!因此,我们的实施策略将因语言而异)。

让我们通过分步层次结构实现来学习这种方法

原型设计模式实现步骤

  1. 我们可以创建一个具有抽象?clone()?方法的抽象类或接口,或者我们可以简单地将该方法添加到所有现有的类层次结构中(如果可用)。
  2. 我们的原型或抽象类将有一个构造函数,它将接受该类的对象作为参数。此构造函数会将所有实例从传递的对象复制到新对象。在更改子类的情况下,它必须调用父构造函数才能从超类克隆私有字段。
  1. 我们在子类中定义的?clone()?方法通常会返回一个具有构造函数原型版本的新对象。在这里,每个类都将显式覆盖克隆方法,否则,它将返回父类的对象
  1. 如果我们经常使用某种特定类型的原型,那么通过创建一个集中的原型注册表来存储它们将是一个好主意。

我们有两种方法可以实现这个注册表,要么我们可以创建一个新的工厂类,要么我们可以简单地在基原型类中实现它,使用静态方法来获取原型。

我们可以借助客户端代码传递给该方法的搜索条件来搜索所需的原型。条件可以是简单的字符串标记或一组参数。找到所需的原型后,注册表将克隆它,并将对象的副本返回给客户端

现在,让我们用一个简单的伪代码来理解原型设计模式。

伪代码

在这里,我们为 Vehicles 创建了伪代码。所有类型的车辆,如两轮车、四轮车、轮胎等,都有一些基本特征,如轮胎数量、平均值、座位数等。

在创建适当车辆类型的对象后,我们的客户(展厅所有者或经理)的最终目标是将已售出的车辆记录存储在数据库中。现在,我们的原型来了。

我们不必担心车辆的类型及其功能。我们的程序正在处理所有这些问题。我们将车辆的所有数据存储在数组名称 - “车辆”中。在出售任何类型的车辆后,我们的程序会调用适当的?clone()?方法并将此数据存储到数组名称 - 'selling vehicle tires.

abstract class Vehicle
    integer seats
    integer tyres
    string color
    string fuel
    
    constructor Vehicle () //Default constructor
    
    constructor Vehicle (source Vehicle) // Parameterized constructor - It will create a new object and initialize this object from the existing object.
        this()
        this.seats = source.seats
        this.tyres = source.tyres
        this.color = source.color
        this.fuel = source.fuel
    
    abstract Vehicle clone () // This method will return the object of one of the subclasses and it will be overridden by the all the subclasses

class Bike extends Vehicle
    integer average
    
    constructor Bike (Bike source)
        super(source)
        this.average = Bike.average
    
    Vehicle clone ()
        return new Bike(this)

class Car extends Vehicle
    integer average
    
    constructor Car(Car source)
        super(source)
        this.average = source.average
    
    Vehicle clone()
        return new Car(this)

//Client code...
class Showroom
    Vehicle[] vehicles
    
    constructor Showroom ()
        Car c1 = new Car()
        c1.seats = 5
        c1.tyres = 4
        c1.color = white
        c1.fuel = petrol
        c1.average = 15
        vehicles.add(c1)

        Car c2 = c1.clone() //C2 object is having exact values as C1
        vehicles.add(c2)

        Bike b1 = new Bike()
        b1.seats = 1
        b1.tyres = 2
        b1.color = black
        b1.fuel = petrol
        b1.average = 50
        vehicles.add(b1)
    
    void cell vehicle () {
        // Here, we can create a copy of the object without knowing anything about it because of the Prototype.
        Vehicles[] vehicle selling of = new Vehicles[10]
        
        //In other words, we are not aware of the exact features related to the Vehicles array. The only information we have is that they all are Vehicles. However, because of Polymorphism, when we call the 'clone()' method on the Vehicle, the program checks for its original class and executes the required clone() method, and hence we get desired clones instead of a set of simple Vehicle objects.
        foreach (vehicle in vehicles) do {
            vehicleSelling.add(vehicle.clone()) // The 'vehicleSelling' array has exact replicas of the 'vehicles' array's children.

如果我们没有在子类中实现 clone()?方法,那么我们将不得不单独维护两个数组,这大大增加了我们的手动工作,有时偶尔会导致我们出现数据错误(两个数组对同一车辆具有不同的数据)。

让我们用不同的编程语言(如JAVA和Python编程语言)来实现这一点

伪代码的 JAVA 实现

import java.util.*;

abstract class Vehicle { // Abstract class having declartion of Clone() method
    // Class Variables
    protected int seats; 
    protected int tyres; 
    protected String color;
    protected String fuel; 

    public Vehicle() { // Default Constructor

    } 
	
    public Vehicle(Vehicle v) { // Parameterized Constructor
        if (v!= null){ // Checking passed object is null or not
            // Copying values from original object into the prototyped object
            this.seats = v.seats;
            this.tyres = v.tyres;
            this.color = v.color;
            this.fuel = v.fuel;
        }
    }
	
    public abstract Vehicle clone(); // Abstract Clone() method - Child classes implement this method 

    @Override
    public String toString() { // Overriding toString() method so that we can get desired print format
        return "Seats: " + this.seats + "\nTyres: " + this.tyres + "\nColor: " + this.color + "\nFuel: " + this.fuel + "\n";
    }
}

class Bike extends Vehicle { // First child class

    public Bike() { // Default Constructor

    }

    public Bike(Bike b) { // Parameterized Constructor 
        super(b); // Calling Parent class to initialize the object
    }

    @Override
    public Vehicle clone() {
        return new Bike(this); // Cloning current object with exact same functionality
    }
}

class Car extends Vehicle { // Second child class

    public Car() {  Default Constructor

    }

    public Car(Car c) { // Parameterized Constructor
        super(c); // Calling Parent class to initialize the object
    }

    @Override
    public Vehicle clone() {
        return new Car(this); // Cloning current object with exact same functionality
    }
}

public class Showroom {
    public static void main(String[] args) {
        List<Vehicle> vehicles = new ArrayList<>(); // List for Original objects
        List<Vehicle> vehiclesCopy = new ArrayList<>(); // List for Cloned objects
        
        Bike b1 = new Bike(); // Demo Bike object
        b1.seats = 1;
        b1.tyres = 2;
        b1.color = "Black";
        b1.fuel = "Petrol";
        vehicles.add(b1);
        
        Car c1 = new Car(); // Demo Car object
        c1.seats = 5;
        c1.tyres = 4;
        c1.color = "White";
        c1.fuel = "Electric";
        vehicles.add(c1);
        
        for (Vehicle vehicle : vehicles) { // cloning each object of 'vehiles' list
            vehiclesCopy.add(vehicle.clone());
        }
        System.out.print("Original Objects:-\n");
        for (Vehicle vehicle : vehicles) { // Printing Original objects
            System.out.println(vehicle.toString());
            System.out.println("---------------------------------------------------------");
        }
        System.out.print("\n\n\nPrototyped Objects:-\n");
        for (Vehicle vehicle : vehiclesCopy) { // Printing cloned objects
            System.out.println(vehicle.toString());
            System.out.println("---------------------------------------------------------");
        }
    }
}

**Output:**
```java

**Original Objects:-**
> Seats: 1
> Tyres: 2
> Color: Black
> Fuel: Petrol
> 
> ---------------------------------------------------------
> Seats: 5
> Tyres: 4
> Color: White
> Fuel: Electric
> 
> ---------------------------------------------------------
> 
> 
> 
> **Prototyped Objects:-**
> Seats: 1
> Tyres: 2
> Color: Black
> Fuel: Petrol
> 
> ---------------------------------------------------------
> Seats: 5
> Tyres: 4
> Color: White
> Fuel: Electric
> 
> ---------------------------------------------------------

伪代码的 Python 实现

from ABC import ABC, abstract method the 
import copy

# Abstract Class
class Vehicle(ABC):
    # Constructor:
    def __init__(self,seats, tyres, color, fuel):
        self.seats = seats
        self.tyres = tyres
        self.color = color
        self.fuel = fuel

    # Clone Method:
    @abstractmethod
    def clone(self):
        pass
    
    # Printing objects as per our requirement:
    def __str__(self):
        return f"Seats = {self.seats}\nTyres = {self.tyres}\nColor = {self.color}\nFuel = {self.fuel}"

# First Child class:
class Bike(Vehicle):
    
    # Constructor:
    def __init__(self, seats, tyres, color, fuel):
        super().__init__(seats, tyres, color, fuel)

    # Overwritting Cloning Method:
    def clone(self):
        return copy.deepcopy(self)

# Second Child class:        
class Car(Vehicle):
    
    # Constructor:
    def __init__(self, seats, tyres, color, fuel):
        super().__init__(seats, tyres, color, fuel)

    # Overwritting Cloning Method:
    def clone(self):
        return copy.deepcopy(self)

# main() method       
def main():
    vehicles = [] # List for Original objects
    vehiclesCopy = [] # List of Prototyped objects
    
    b1 = Bike(1,2, 'Black', 'Petrol') # Creating Bike object
    c1 = Car(5,4, 'White', 'Electric') # Creating Car object
    
    vehicles.append(b1) # Adding Bike object into the List
    vehicles.append(c1) # Adding Car object into the List
    
    b1Copy = b1.clone() # Clonning Bike object
    c1Copy = c1.clone() # Clonning Car object
    
    vehiclesCopy.append(b1Copy) # Adding Clonned Bike object into the List
    vehiclesCopy.append(c1Copy) # Adding Clonned Car object into the List
    
    print("Original Objects:")
    for v in vehicles:# Printing Original Objects
        print(v)
        print("---------------------------------------------------")
    print("\n\n\nPrototyped Objects:")
    for vC in vehiclesCopy: # Printing Prototypes Objects
        print(vC)
        print("---------------------------------------------------")
    
if __name__=="__main__":
    main()

输出:

>**Original Objects:**
>Seats = 1
>Tyres = 2
>Color = Black
>Fuel = Petrol
>
>------------------------------------------------
>
>Seats = 5
>Tyres = 4
>Color = White
>Fuel = Electric
>
> ------------------------------------------------
>
>
>
>**Prototyped Objects:**
>Seats = 1
>Tyres = 2
>Color = Black
>Fuel = Petrol
>
> -------------------------------------------------
>Seats = 5
>Tyres = 4
>Color = White
>Fuel = Electric
>
>--------------------------------------------------

从上面的代码输出中,我们可以更清楚地了解如何实现以不同语言创建原型的功能。

如果我们仔细观察这两种特定于语言的实现,我们将能够理解我们在?JAVA 和 Python?的代码中使用了相同的逻辑,这两种语言都有内置的?clone()?方法。

在下一节中,我们将了解原型设计模式的一些优点和缺点。

原型设计模式的优缺点。

原型设计模式的优点 -

  1. 在运行时添加和删除对象 -原型允许我们在现有系统中创建新的具体对象类,只需向客户注册所需的原型实例(意味着、实现、基于原始对象的原型,但根据要求具有有限的功能、特性和属性)。与其他创建模式相比,它对最终用户来说更灵活,因为他们可以在运行时轻松安装和卸载原型。
  2. 创建具有不同值的新对象 -通过这种模式,我们可以通过对象组合来定义新的行为,方法是将结构更改为对象变量的值,而不是为此创建单独的类。
  3. 创建具有不同结构的新对象 -在现实生活中,很多时候,我们使用现有类和对象的部分和子部分来创建具有不同结构的新对象和类。
  4. 子类数量较少 -通过使用传统方法,我们必须创建一系列子类来实现我们的最终目标。但是,原型模式允许我们从现有对象创建克隆,从而消除为新对象创建新类。
  5. 继承的替代选项 -如果我们的需求是具有复杂的对象结构,我们可以使用原型设计模式作为继承选项。它可以显着减少代码中的总行数,并提高其可读性和可理解性。
  6. 减少手动初始化次数 -原型设计模式允许我们使用原始对象中的现有值来初始化克隆对象的值。因此,我们不需要填充克隆对象的所有值,我们只需要添加/更改值所需的参数即可。

原型设计模式的缺点 -

  1. 对于需要较少对象数量的项目,实现原型模式可能有点矫枉过正。
  2. 它向客户端隐藏了对象/程序的实际结构。
  3. 当我们使用原型设计模式时,每个子类都必须实现?clone()?方法,在某些情况下,这种实现可能很困难。
  4. 当对象相互循环引用时,不建议使用原型设计模式。(因为在这种类型的对象中,存在对其他对象的依赖关系。现在,如果我们想为一个对象创建一个原型,我们可能还需要为其他依赖对象创建原型,我们不需要,但是由于它们都在协同工作以实现我们可能需要的最终目标,这会导致复杂性的增加

原型设计模式与其他类似模式的区别

  • 大多数时候,设计从工厂方法设计模式开始(因为使用子类可以降低复杂性和高度自定义),然后走向抽象工厂原型构建器设计模式(高度的灵活性和复杂性)。
  • 在许多情况下,抽象工厂类(使用抽象工厂模式实现)是基于工厂方法设计模式创建的。但是,也可以使用原型设计模式为这些类创建方法。
  • 当我们需要在历史记录中保存命令设计模式的副本时,原型会很有帮助。
  • 项目设计主要基于复合和/或装饰器,使用原型设计模式可以从中受益,因为我们可以直接克隆复杂的结构,而不是从头开始创建它们。
  • 原型不是基于继承的,所以它们没有继承的缺点。但是,它们对克隆对象的初始化步骤很复杂。工厂方法基于继承,它们不需要单独的初始化步骤。
  • 有时,如果对象的状态(我们希望保留在历史记录中)并不复杂且没有与外部资源的链接,或者重新建立这些链接很简单,则与?Memento?相比,原型设计模式更简单。
  • 可以将抽象工厂构建器原型实现为单例

常见问题

问:简要解释一下原型设计模式?

一个:原型设计模式为我们提供了一种使用现有对象创建新对象的方法。换句话说,我们可以使用此模式从现有对象复制数据并将其存储在新对象中。

问:解释原型设计模式中的浅拷贝和深拷贝?

一个:浅拷贝和深拷贝是原型设计模式中的克隆类型。在浅拷贝中,我们只克隆了父对象,而不克隆了它的包含对象,而在深拷贝中,我们克隆了父对象及其包含对象。

文章来源:https://blog.csdn.net/mzgxinhua/article/details/135348253
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。