设计模式-创新型模式

简介

设计模式是软件开发中的一种通用解决方案,它们是经过实践证明的、被广泛接受的最佳实践。设计模式提供了一种可重用的设计思想,可以帮助开发人员解决常见的设计问题,提高代码质量和可维护性。

设计模式通常包含以下元素:

  1. 模式名称:用于描述模式的名称。

  2. 问题描述:描述模式所解决的问题,包括场景和限制条件。

  3. 解决方案:描述模式的解决方案,包括结构、参与者、协作方式和责任。

  4. 优点和缺点:描述模式的优点和缺点,包括可维护性、可扩展性、可重用性等方面。

  5. 适用性:描述模式适用的场景和限制条件。

  6. 实现方式:描述模式的实现方式,包括代码示例和实现细节。

创新型模式

简单工厂模式

简介

  • 简单工厂模式又叫静态方法模式(因为工厂类定义了一个静态方法)
    现实生活中,工厂是负责生产产品的;
  • 同样在设计模式中,简单工厂模式我们可以理解为负责生产对象的一个类,称为“工厂类”。

解决问题

  • 将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产的细节。
  • 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
  • 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。

模式原理

组成 关系 作用
抽象产品 具体产品的父类 描述产品的公共接口
具体产品 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
工厂 被外界调用 根据传入不同参数从而创建不同具体产品类的实例

UML类图

img

举例实现

abstract class Product{
public abstract void Show();
}
//具体产品类A
class ProductA extends Product{

@Override
public void Show() {
System.out.println("生产出了产品A");
}
}

//具体产品类B
class ProductB extends Product{

@Override
public void Show() {
System.out.println("生产出了产品C");
}
}

//具体产品类C
class ProductC extends Product{

@Override
public void Show() {
System.out.println("生产出了产品C");
}
}
class  Factory {
public static Product Manufacture(String ProductName){
//工厂类里用switch语句控制生产哪种商品;
//使用者只需要调用工厂类的静态方法就可以实现产品类的实例化。
switch (ProductName){
case "A":
return new ProductA();

case "B":
return new ProductB();

case "C":
return new ProductC();

default:
return null;

}
}
}
//工厂产品生产流程
public class SimpleFactoryPattern {
public static void main(String[] args){
Factory mFactory = new Factory();

//客户要产品A
try {
//调用工厂类的静态方法 & 传入不同参数从而创建产品实例
mFactory.Manufacture("A").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}

//客户要产品B
try {
mFactory.Manufacture("B").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}

//客户要产品C
try {
mFactory.Manufacture("C").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}

//客户要产品D
try {
mFactory.Manufacture("D").Show();
}catch (NullPointerException e){
System.out.println("没有这一类产品");
}
}
}
生产出了产品A
生产出了产品C
生产出了产品C
没有这一类产品

问题

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
  • 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
  • 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

工厂方法模式

简介

  • 工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
  • 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。

解决问题

  • 具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点
  • 新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可
  • 符合单一职责原则,每个具体工厂类只负责创建对应的产品
  • 不使用静态工厂方法,可以形成基于继承的等级结构。简单工厂模式的工厂类使用静态工厂方法

模式组成

组成 关系 作用
抽象产品 具体产品的父类 描述具体产品的公共接口
具体产品 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂 具体工厂的父类 描述具体工厂的公共接口
具体工厂 抽象工厂的子类;被外界调用 描述具体工厂;实现FactoryMethod工厂方法创建产品的实例

UML类图

image-20230326144728389

实例举例

abstract class Factory{
public abstract Product Manufacture();
}
abstract class Product{
public abstract void Show();
}
//具体产品A类
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生产出了产品A");
}
}

//具体产品B类
class ProductB extends Product{

@Override
public void Show() {
System.out.println("生产出了产品B");
}
}
//工厂A类 - 生产A类产品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}

//工厂B类 - 生产B类产品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}
//生产工作流程
public class FactoryPattern {
public static void main(String[] args){
//客户要产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();

//客户要产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}
生产出了产品A
生产出了产品C

问题

  • 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度
  • 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;

应用

  • 当一个类不知道它所需要的对象的类时,在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
  • 当一个类希望通过其子类来指定创建对象时,在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
  • 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

抽象工厂模式

简介

  • 抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
  • 抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类
  • 允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。

解决问题

  • 降低耦合,抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展;
  • 更符合开-闭原则,新增一种产品类时,只需要增加相应的具体产品类和相应的工厂子类即可

模式组成

组成 关系 作用
抽象产品族 抽象产品的父类 描述抽象产品的公共接口
抽象产品 具体产品的父类 描述具体产品的公共接口
具体产品 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂 具体工厂的父类 描述具体工厂的公共接口
具体工厂 抽象工厂的子类;被外界调用 描述具体工厂;实现工厂方法创建产品的实例

UML类图

img

举例实例

abstract class Factory{
public abstract Product ManufactureContainer();
public abstract Product ManufactureMould();
}
abstract class AbstractProduct{
public abstract void Show();
}
//容器产品抽象类
abstract class ContainerProduct extends AbstractProduct{
@Override
public abstract void Show();
}

//模具产品抽象类
abstract class MouldProduct extends AbstractProduct{
@Override
public abstract void Show();
}
//容器产品A类
class ContainerProductA extends ContainerProduct{
@Override
public void Show() {
System.out.println("生产出了容器产品A");
}
}

//容器产品B类
class ContainerProductB extends ContainerProduct{
@Override
public void Show() {
System.out.println("生产出了容器产品B");
}
}

//模具产品A类
class MouldProductA extends MouldProduct{

@Override
public void Show() {
System.out.println("生产出了模具产品A");
}
}

//模具产品B类
class MouldProductB extends MouldProduct{

@Override
public void Show() {
System.out.println("生产出了模具产品B");
}
}
//A厂 - 生产模具+容器产品
class FactoryA extends Factory{

@Override
public Product ManufactureContainer() {
return new ContainerProductA();
}

@Override
public Product ManufactureMould() {
return new MouldProductA();
}
}

//B厂 - 生产模具+容器产品
class FactoryB extends Factory{

@Override
public Product ManufactureContainer() {
return new ContainerProductB();
}

@Override
public Product ManufactureMould() {
return new MouldProductB();
}
}
//生产工作流程
public class AbstractFactoryPattern {
public static void main(String[] args){
FactoryA mFactoryA = new FactoryA();
FactoryB mFactoryB = new FactoryB();
//A厂当地客户需要容器产品A
mFactoryA.ManufactureContainer().Show();
//A厂当地客户需要模具产品A
mFactoryA.ManufactureMould().Show();

//B厂当地客户需要容器产品B
mFactoryB.ManufactureContainer().Show();
//B厂当地客户需要模具产品B
mFactoryB.ManufactureMould().Show();

}
}
生产出了容器产品A
生产出了容器产品B
生产出了模具产品A
生产出了模具产品B

问题

  • 抽象工厂模式很难支持新种类产品的变化

单例模式

简介

  • 实现1个类只有1个实例化对象 & 提供一个全局访问点
  • 从上面可看出:工人类操作的明显不是同一个仓库实例,而全部工人希望操作的是同一个仓库实例,即只有1个实例

工作原理

  • 在Java中,我们通过使用对象(类实例化后)来操作这些类,类实例化是通过它的构造方法进行的,要是想实现一个类只有一个实例化对象。

img

举例实现

 public class Singleton {
//1. 创建私有变量 ourInstance(用以记录 Singleton 的唯一实例)
//2. 内部进行实例化
private static Singleton ourInstance = new Singleton();

//3. 把类的构造方法私有化,不让外部调用构造方法实例化
private Singleton() {
}
//4. 定义公有方法提供该类的全局唯一访问点
//5. 外部通过调用getInstance()方法来返回唯一的实例
public static Singleton newInstance() {
return ourInstance;
}
}

情景代入

背景:小成有一个塑料生产厂,但里面只有一个仓库。
目的:想用代码来实现仓库的管理
现有做法: 建立仓库类和工人类 其中,仓库类里的quantity=商品数量;工人类里有搬运方法MoveIn(int i)和MoveOut(int i)。

一般实现
//仓库类
class StoreHouse {
private int quantity = 100;

public void setQuantity(int quantity) {
this.quantity = quantity;
}

public int getQuantity() {
return quantity;
}
}

//搬货工人类
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬货进仓库
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬货出仓库
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}

//工人搬运测试
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = new StoreHouse();
StoreHouse mStoreHouse2 = new StoreHouse();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);

System.out.println("两个是不是同一个?");

if(mStoreHouse1.equals(mStoreHouse2)){//这里用equals而不是用 == 符号,因为 == 符号只是比较两个对象的地址
System.out.println("是同一个");
}else {
System.out.println("不是同一个");
}
//搬运工搬完货物之后出来汇报仓库商品数量
Carrier1.MoveIn(30);
System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}
单例实现
package scut.designmodel.SingletonPattern;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//单例仓库类
class StoreHouse {

//仓库商品数量
private int quantity = 100;
//自己在内部实例化
private static StoreHouse ourInstance = new StoreHouse();;
//让外部通过调用getInstance()方法来返回唯一的实例。
public static StoreHouse getInstance() {
return ourInstance;
}

//封闭构造函数
private StoreHouse() {
}

public void setQuantity(int quantity) {
this.quantity = quantity;
}

public int getQuantity() {
return quantity;
}
}


//搬货工人类
class Carrier{
public StoreHouse mStoreHouse;
public Carrier(StoreHouse storeHouse){
mStoreHouse = storeHouse;
}
//搬货进仓库
public void MoveIn(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
}
//搬货出仓库
public void MoveOut(int i){
mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
}
}

//工人搬运测试
public class SinglePattern {
public static void main(String[] args){
StoreHouse mStoreHouse1 = StoreHouse.getInstance();
StoreHouse mStoreHouse2 = StoreHouse.getInstance();
Carrier Carrier1 = new Carrier(mStoreHouse1);
Carrier Carrier2 = new Carrier(mStoreHouse2);

System.out.println("两个是不是同一个?");

if(mStoreHouse1.equals(mStoreHouse2)){
System.out.println("是同一个");
}else {
System.out.println("不是同一个");
}
//搬运工搬完货物之后出来汇报仓库商品数量
Carrier1.MoveIn(30);
System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
Carrier2.MoveOut(50);
System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
}
}

多种单例模式实现方式

饿汉式
  • 依赖JVM类加载机制,保证单例只会被创建一次,即线程安全
  • JVM在类的初始化阶段,会执行类的初始化
  • 在执行类的初始化的时候,JVM会去获取一个锁,这个所可以同步多个线程对同一个类的初始化
    class Singleton {

    // 1. 加载该类时,单例就会自动被创建
    private static Singleton ourInstance = new Singleton();

    // 2. 构造函数 设置为 私有权限
    // 原因:禁止他人创建实例
    private Singleton() {
    }

    // 3. 通过调用静态方法获得创建的单例
    public static Singleton newInstance() {
    return ourInstance;
    }
    }
    枚举类型
  • 根据枚举类型的下述特点,满足单例模式所需的创建单例,线程安全,实现简介的需求
  • img
public enum Singleton{

//定义1个枚举的元素,即为单例类的1个实例
INSTANCE;

// 隐藏了1个空的、私有的 构造方法
// private Singleton () {}

}

// 获取单例的方式:
Singleton singleton = Singleton.INSTANCE;
懒汉式
  • 单例创建时机可控,有需要的时候才手动创建单例
class Singleton {
// 1. 类加载时,先不自动创建单例
// 即,将单例的引用先赋值为 Null
private static Singleton ourInstance = null

// 2. 构造函数 设置为 私有权限
// 原因:禁止他人创建实例
private Singleton() {
}

// 3. 需要时才手动调用 newInstance() 创建 单例
public static Singleton newInstance() {
// 先判断单例是否为空,以避免重复创建
if( ourInstance == null){
ourInstance = new Singleton();
}
return ourInstance;
}
}

问题

  • 对于懒汉会存在线程不安全的时候,特别是在多线程时候会出现以下问题
  • img

同步锁

  • 使用同步锁synchronized,锁住创建单例的方法,防止多个线程同时调用,从而避免造成单例被多次创建
  • getInstance()方法块只能运行在一个线程中
  • 倘若此段代码已在一个线程中运行,则另外一个线程试图运行该块代码,则会被一直阻塞二一直等待。
    实现
    // 写法1
    class Singleton {
    // 1. 类加载时,先不自动创建单例
    // 即,将单例的引用先赋值为 Null
    private static Singleton ourInstance = null

    // 2. 构造函数 设置为 私有权限
    // 原因:禁止他人创建实例
    private Singleton() {
    }

    // 3. 加入同步锁
    public static synchronized Singleton getInstance(){
    // 先判断单例是否为空,以避免重复创建
    if ( ourInstance == null )
    ourInstance = new Singleton();
    return ourInstance;
    }
    }


    // 写法2
    // 该写法的作用与上述写法作用相同,只是写法有所区别
    class Singleton{

    private static Singleton instance = null;

    private Singleton(){
    }

    public static Singleton getInstance(){
    // 加入同步锁
    synchronized(Singleton.class) {
    if (instance == null)
    instance = new Singleton();
    }
    return instance;
    }
    }

双重校验锁

  • 在同步锁的基础上,添加多一层if判断,若单例已经创建,则不需要执行加锁操作就可以获取实例,从而提高性能
实现
class Singleton {
private static Singleton ourInstance = null

private Singleton() {
}

public static Singleton newInstance() {
// 加入双重校验锁
// 校验锁1:第1个if
if( ourInstance == null){ // ①
synchronized (Singleton.class){ // ②
// 校验锁2:第2个 if
if( ourInstance == null){
ourInstance = new Singleton();
}
}
}
return ourInstance;
}
}

// 说明
// 校验锁1:第1个if
// 作用:若单例已创建,则直接返回已创建的单例,无需再执行加锁操作
// 即直接跳到执行 return ourInstance

// 校验锁2:第2个 if
// 作用:防止多次创建单例问题
// 原理
// 1. 线程A调用newInstance(),当运行到②位置时,此时线程B也调用了newInstance()
// 2. 因线程A并没有执行instance = new Singleton();,此时instance仍为空,因此线程B能突破第1层 if 判断,运行到①位置等待synchronized中的A线程执行完毕
// 3. 当线程A释放同步锁时,单例已创建,即instance已非空
// 4. 此时线程B 从①开始执行到位置②。此时第2层 if 判断 = 为空(单例已创建),因此也不会创建多余的实例

静态内部类

  • 更具静态内部类的特性,同步解决按需加载,线程安全的问题,同时实现简洁
  • 在静态内部类中创建单例,在装载该内部类的时候才会去创建单例
  • 线程安全:类是由JVM加载,而JVM只会加载一遍,保证只有一个单例
实现
class Singleton {

// 1. 创建静态内部类
private static class Singleton2 {
// 在静态内部类里创建单例
private static Singleton ourInstance = new Singleton();
}

// 私有构造函数
private Singleton() {
}

// 延迟加载、按需创建
public static Singleton newInstance() {
return Singleton2.ourInstance;
}

}

// 调用过程说明:
// 1. 外部调用类的newInstance()
// 2. 自动调用Singleton2.ourInstance
// 2.1 此时单例类Singleton2得到初始化
// 2.2 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例;
// 2.3 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性
// 3. 最终只创建1个单

总结

img

建造者模式

简介

  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  • 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
  • 建造者模式负责按照是顺序创建复杂对象(把内部的建造过程和细节隐藏藏匿起来)

解决问题

  • 方便用户创建复杂的对象
  • 代码复用性以及封装性(将对象构建过程和细节进行封装以及复用)
  • 方便解耦,方便控制对象的创建,方便于拓展。

UML类图

img

模式讲解

  1. 指挥者直接和客户进行需求沟通
  2. 沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求
  3. 将各个部件的建造请求委派到具体的建造者
  4. 各个具体建造者负责进行产品部件的构建
  5. 最终构建成具体产品。

情景代入

  • 背景:小成希望去电脑城买一台组装的台式主机
  • 过程:
  1. 电脑城老板(Diretor)和小成(Client)进行需求沟通(买来打游戏?学习?看片?)
  2. 了解需求后,电脑城老板将小成需要的主机划分为各个部件(Builder)的建造请求(CPU、主板blabla)
  3. 指挥装机人员(ConcreteBuilder)去构建组件;
  4. 将组件组装起来成小成需要的电脑(Product);
举例实例
  1. 定义组装的过程:组装电脑的过程
    public abstract class Builder{
    //第一步:CPU
    //声明为抽象方法,具体由子类实现
    public abstract void BuildCPU();
    //第二步:装主板
    //声明为抽象方法,具体由子类实现
    public abstract void BuildMainboard();
    //第三步:装硬盘
    //声明为抽象方法,具体由子类实现
    public abstract void BuildHD();
    //返回产品的方法:获得组装好的电脑
    public abstract Computer GetCompputer();
    }
  2. 电脑城老板委派任务给装机人员
    public class Director{
    //指挥装机人员组装的电脑
    public void Construct(Builder builder){
    builder.BuildCPU();
    builder.BuildMainboard();
    builder.BuuildHD();
    }
    }
  3. 创建具体的建造者:装机人员
    public class ConcreteBuilder extends Builder {
    //创建产品实例
    Computer computer = new Computer();

    //组装产品
    @Override
    public void BuildCPU(){
    computer.Add("组装CPU");
    }
    @Override
    public void BuilMainboard(){
    computer.Add("组装主板");
    }
    @Override
    public void BuildHD(){
    computer.Add("组装硬盘");
    }
    //返回组装成功的电脑
    @Override
    public Computer GetComputer(){
    return computer;
    }
    }
  4. 定义具体产品类:电脑
    public class Computer{
    //电脑组件的集合
    private List<String> parts = new ArrayList<String>();
    //用于将组件组装到电脑
    public void Add(String part){
    part.add (part);
    }
    public void Show(){
    for(int i =0;<part.size();i++){
    System.out.println("组件"+part.get(i)+"装好了");
    }
    System.out.println("电脑组装完成,请验收");
    }
    }
  5. 客户端调用-小成到电脑找了老板买电脑
public class Builder Pattern{
public static void main(String[] args){

//逛了很久终于发现一家合适的电脑店
//找到该店的老板和装机人员
Director director = new Director();
Builder builder = new ConcreteBuilder();

//沟通需求后,老板叫装机人员去装电脑
director.Construct(builder);

//装完后,组装人员搬来组装好的电脑
Computer computer = builder.GetComputer();
//组装人员展示电脑给小成看
computer.Show();

}

}

问题

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

原型模式

简介

  • 它允许通过复制现有的对象来创建新的对象,而不是通过创建新的实例并初始化它们来创建对象。这种模式通常用于创建具有相同属性的多个对象,以避免重复的初始化过程。在原型模式中,原型对象是创建新对象的模板,新对象是通过复制原型对象来创建的。原型模式的实现通常需要实现 Cloneable 接口或者使用序列化和反序列化来实现对象的复制。

UML类图

image-20230327104312924

解决问题

  • 逃避构造函数的约束。

举例实例

public abstract class Shape implements Cloneable {

private String id;
protected String type;

abstract void draw();

public String getType(){
return type;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
public class Rectangle extends Shape {

public Rectangle(){
type = "Rectangle";
}

@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square extends Shape {

public Square(){
type = "Square";
}

@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle extends Shape {

public Circle(){
type = "Circle";
}

@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
import java.util.Hashtable;

public class ShapeCache {

private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();

public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}

// 对每种形状都运行数据库查询,并创建该形状
// shapeMap.put(shapeKey, 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 class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();

Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());

Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());

Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}

问题

  • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很男
  • 必须实现Cloneable接口

应用

  • 资源优化
  • 类初始化需要消化很多资源
  • 一个对象多个修改者