原型设计模式是创建性设计模式之一。它允许我们指定作为原始对象原型的对象。换句话说,它允许我们复制现有对象而不是创建新实例。
在这里,我们将详细了解原型设计模式及其用法。
原型设计模式主要用于从头开始创建新对象的成本很高,并且我们已经有类似的类来创建此类对象。
在原型设计模式的帮助下,我们从现有的类中复制新对象,并根据我们的新要求修改这些对象。
假设我们有一个程序用于将数据从一个特定来源插入数据库。
现在,我们收到了一个新要求,在将数据插入数据库之前,我们需要执行某种类型的数据预处理和数据修改。由于我们已经有一个用于插入数据的程序,因此我们可以使用此对象并对其进行一些更改,以便它可以修改/预处理数据,然后将其插入数据库。
为此,我们将现有对象克隆到新对象中,并对这个新对象进行必要的更改。
对于原型设计模式,必须在现有对象中具有复制功能。如果我们想创建对象的原型,我们需要为该对象/类实现/定义一个?clone()?方法。虽然,这取决于我们的设计和要求,是使用现有对象的浅拷贝还是深拷贝。
浅拷贝 - 在浅拷贝中,我们只克隆父对象,而不克隆其包含对象。
深层复制?- 在深层复制中,我们克隆父对象及其包含对象。
手机制造公司为各个细分市场发布不同的手机。现在,每个手机都将具有一些基本的通用功能,但有一定程度的变化。在这里,该公司将使用其手机软件之一,并根据其他手机进行克隆。
笔记本电脑制造公司在软件中为一台笔记本电脑设计了笔记本电脑的机身。之后,对于这款笔记本电脑型号的未来版本,该公司将简单地克隆上一个型号的机身,并根据最新趋势和要求进行必要的更改。
在这里,我们了解了原型设计模式在实际场景中的工作原理。此外,我们将看到它将如何工作及其内部实现。
- 从上图中我们可以了解到,clone()?方法是在?Prototype 接口中声明的。在大多数情况下,只有一个?clone()?方法。
- 此?clone() 方法在?Concrete Prototype?类(以绿色表示)中实现。在这里,clone()?方法将数据从原始对象复制到克隆对象中。此外,此处还管理与复制对象相关的边界值条件。
- 客户端(以蓝色表示)可以在不实现底层代码的情况下创建原始对象的副本。
让我们了解原型设计模式的详细实现。
首先,原型允许我们从客户端代码中隐藏创建新对象实例的复杂性。这背后的基本概念是复制现有对象,而不是从头开始创建新的对象实例,该实例可能具有一些复杂的操作或业务逻辑。简单来说,通过使用这种设计模式,最终用户不需要担心原始对象的内部实现,也不需要检查原始对象的每个属性并将其分配给复制的对象。
因此,现有对象表示为 Prototype 接口并包含一个基对象。如果需要,新创建的子类可以包含外部元素和方法。如果我们想改变它的功能,我们可以向原型对象添加更多的功能/类。
当对象开发是一项耗时且成本高昂的操作时,这种原型模式是必要的,因此我们可以使用现有对象实例本身创建对象。
在?Java 和 Python?中,内置的 Clone()?方法是从现有对象创建对象的最佳可用选项。这是实现原型模式的最简单方法。但是,原型的实现可能会根据业务需求和语言而变化(例如,在 C++ 中,我们没有任何内置方法来克隆对象!因此,我们的实施策略将因语言而异)。
让我们通过分步层次结构实现来学习这种方法。
- 我们可以创建一个具有抽象?clone()?方法的抽象类或接口,或者我们可以简单地将该方法添加到所有现有的类层次结构中(如果可用)。
- 我们的原型或抽象类将有一个构造函数,它将接受该类的对象作为参数。此构造函数会将所有实例从传递的对象复制到新对象。在更改子类的情况下,它必须调用父构造函数才能从超类克隆私有字段。
- 我们在子类中定义的?clone()?方法通常会返回一个具有构造函数原型版本的新对象。在这里,每个类都将显式覆盖克隆方法,否则,它将返回父类的对象。
- 如果我们经常使用某种特定类型的原型,那么通过创建一个集中的原型注册表来存储它们将是一个好主意。
我们有两种方法可以实现这个注册表,要么我们可以创建一个新的工厂类,要么我们可以简单地在基原型类中实现它,使用静态方法来获取原型。
我们可以借助客户端代码传递给该方法的搜索条件来搜索所需的原型。条件可以是简单的字符串标记或一组参数。找到所需的原型后,注册表将克隆它,并将对象的副本返回给客户端。
现在,让我们用一个简单的伪代码来理解原型设计模式。
在这里,我们为 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编程语言)来实现这一点。
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
>
> ---------------------------------------------------------
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()?方法。
在下一节中,我们将了解原型设计模式的一些优点和缺点。
问:简要解释一下原型设计模式?
一个:原型设计模式为我们提供了一种使用现有对象创建新对象的方法。换句话说,我们可以使用此模式从现有对象复制数据并将其存储在新对象中。
问:解释原型设计模式中的浅拷贝和深拷贝?
一个:浅拷贝和深拷贝是原型设计模式中的克隆类型。在浅拷贝中,我们只克隆了父对象,而不克隆了它的包含对象,而在深拷贝中,我们克隆了父对象及其包含对象。