设计模式之创建型

时间:2020-12-22 01:22:39


  • 创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。
  • 创建型模式分为以下几种。
  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
  • 以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式。

工厂模式

  • 在众多设计模式当中,有一种被称为工厂模式的设计模式,它提供了创建对象的最佳方式。工厂模式可以分为:简单工厂模式、工厂方法模式和抽象工厂模式。
  • 简单工厂模式又叫 静态方法模式,因为工厂类中定义了一个静态方法用于创建对象。简单工厂让使用者不用知道具体的参数就可以创建出所需的 ”产品“ 类,即使用者可以直接消费产品而不需要知道产品的具体生产细节。
  • 工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫多态工厂(Polymorphic Factory)模式,在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象, 这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
  • 抽象工厂模式(Abstract Factory Pattern),提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。

简单工厂

  • 意图:在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。
  • 何时使用:我们明确地计划不同条件下创建不同实例时。
  • 优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
  • 缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

实现

设计模式之创建型

步骤 1 创建一个接口:
Shape.java
public interface Shape {
   void draw();
}

步骤 2: 创建实现接口的实体类。
Rectangle.java
public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

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

步骤 3 创建一个工厂,生成基于给定信息的实体类的对象。
ShapeFactory.java
public class ShapeFactory {
   //使用 getShape 方法获取形状类型的对象
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
}

步骤 4: 使用该工厂,通过传递类型信息来获取实体类的对象。
FactoryPatternDemo.java
public class FactoryPatternDemo {
   public static void main(String[] args) {
   
      ShapeFactory shapeFactory = new ShapeFactory();
      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");
      //调用 Rectangle 的 draw 方法
      shape2.draw();
 
      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");
      //调用 Square 的 draw 方法
      shape3.draw();
   }
}

工厂方法

  • 意图:定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
  • 在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。
  • 优缺点:
  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护。
  • 开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型。
  • 应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。

实现

设计模式之创建型

public abstract class Factory {
    abstract public Product factoryMethod();
    public void doSomething() {
        Product product = factoryMethod();
        // do something with the product
    }
}
  
public class ConcreteFactory1 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct1();
    }
}
  
public class ConcreteFactory2 extends Factory {
    public Product factoryMethod() {
        return new ConcreteProduct2();
    }
}


factory1 = new ConcreteFactory1();
factory2 = new ConcreteFactory2();

Product product1 = factory1. factoryMethod();
Product product2 = factory2. factoryMethod();

product1. doSomething();
product2. doSomething();

抽象工厂模式

  • 抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
  • 意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
  • 关键代码:在一个工厂里聚合多个同类产品。
  • 优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
  • 你可以确保同一工厂生成的产品相互匹配。
  • 你可以避免客户端和具体产品代码的耦合
  • 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
  • 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。
  • 缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

实现

设计模式之创建型

设计模式之创建型

步骤 1 为形状创建一个接口。
Shape.java
public interface Shape {
   void draw();
}

步骤 2 创建实现接口的实体类。
Rectangle.java
public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

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

步骤 3 为颜色创建一个接口。
Color.java
public interface Color {
   void fill();
}

步骤4 创建实现接口的实体类。
Red.java
public class Red implements Color { 
   @Override
   public void fill() {
      System.out.println("Inside Red::fill() method.");
   }
}

Green.java
public class Green implements Color { 
   @Override
   public void fill() {
      System.out.println("Inside Green::fill() method.");
   }
}

步骤 5 为 Color 和 Shape 对象创建抽象类来获取工厂。
AbstractFactory.java
public abstract class AbstractFactory {
   public abstract Color getColor(String color);
   public abstract Shape getShape(String shape);
}

步骤 6 创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。
ShapeFactory.java
public class ShapeFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
     if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }
   
   @Override
   public Color getColor(String color) {
      return null;
   }
}

ColorFactory.java
public class ColorFactory extends AbstractFactory {
   @Override
   public Shape getShape(String shapeType){
      return null;
   }
   
   @Override
   public Color getColor(String color) {
      if(color == null){
         return null;
      }        
      if(color.equalsIgnoreCase("RED")){
         return new Red();
      } else if(color.equalsIgnoreCase("GREEN")){
         return new Green();
      } 
      return null;
   }
}

步骤 7 创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。
FactoryProducer.java
public class FactoryProducer {

   public static AbstractFactory getFactory(String choice){
      if(choice.equalsIgnoreCase("SHAPE")){
         return new ShapeFactory();
      } else if(choice.equalsIgnoreCase("COLOR")){
         return new ColorFactory();
      }
      return null;
   }
}

步骤 8 使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象。
AbstractFactoryPatternDemo.java
public class AbstractFactoryPatternDemo {
   public static void main(String[] args) {
 
      //获取形状工厂
      AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
 
      //获取形状为 Circle 的对象
      Shape shape1 = shapeFactory.getShape("CIRCLE");
      //调用 Circle 的 draw 方法
      shape1.draw();
  
      //获取颜色工厂
      AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
 
      //获取颜色为 Red 的对象
      Color color1 = colorFactory.getColor("RED");
      //调用 Red 的 fill 方法
      color1.fill();
    }
}

单例设计模式

  • 单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
  • 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
  • 主要解决:一个全局使用的类频繁地创建与销毁。
  • 何时使用:当您想控制实例数目,节省系统资源的时候。
  • 关键代码:构造函数是私有的。
  • 单例模式的优点:
  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。
  • 单例模式的缺点:
  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
  • 应用场景
  • 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  • 某类只要求生成一个对象的时候。
  • 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  • 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
  • 频繁访问数据库或文件的对象。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如数据库的连接池等。

实现

  • 单例模式的主要角色如下。
  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类:使用单例的类。

设计模式之创建型

  • 经验之谈:一般情况下,不建议使用懒汉方式,建议使用饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。

懒汉式单例

  • 该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。
  • 是否 Lazy 初始化:是
  • 是否多线程安全:否
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}
  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 优点:第一次调用才初始化,避免内存浪费。
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率
public class LazySingleton {
    private static volatile LazySingleton instance = null;    //保证 instance 在所有线程中同步
    private LazySingleton() {
    }    //private 避免类在外部被实例化
    
    public static synchronized LazySingleton getInstance() {
        //getInstance 方法前加同步
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

饿汉式单例

  • 该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了
  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。
public class HungrySingleton {
    private static final HungrySingleton instance = new HungrySingleton();
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance() {
        return instance;
    }
}

双检锁/双重校验锁(DCL,即 double-checked locking)

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

登记式/静态内部类

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。
  • 这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

枚举

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

建造者模式

  • 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
  • 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
  • 主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
  • 关键代码:建造者:创建和提供实例,   导演:管理建造出来的实例的依赖关系。
  • 优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
  • 你可以分步创建对象, 暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时, 你可以复用相同的制造代码。
  • 单一职责原则。 你可以将复杂构造代码从产品的业务逻辑中分离出来。
  • 缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
  • 使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
  • 注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

实现

设计模式之创建型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eMp1gJ9C-1665329905252)(https://www.runoob.com/wp-content/uploads/2014/08/20210315-builder-pattern.svg#crop=0&crop=0&crop=1&crop=1&height=504&id=xd1IG&originHeight=676&originWidth=804&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=&width=599)]

步骤 1 创建一个表示食物条目和食物包装的接口。
Item.java
public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}

Packing.java
public interface Packing {
   public String pack();
}

步骤 2 创建实现 Packing 接口的实体类。
Wrapper.java
public class Wrapper implements Packing {
   @Override
   public String pack() {
      return "Wrapper";
   }
}

Bottle.java
public class Bottle implements Packing { 
   @Override
   public String pack() {
      return "Bottle";
   }
}

步骤 3 创建实现 Item 接口的抽象类,该类提供了默认的功能。
Burger.java
public abstract class Burger implements Item {
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}

ColdDrink.java
public abstract class ColdDrink implements Item {
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}

步骤 4 创建扩展了 Burger 和 ColdDrink 的实体类。
VegBurger.java
public class VegBurger extends Burger {
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}

ChickenBurger.java
public class ChickenBurger extends Burger {
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}

Coke.java
public class Coke extends ColdDrink { 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}

Pepsi.java
public class Pepsi extends ColdDrink { 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}

步骤 5 创建一个 Meal 类,带有上面定义的 Item 对象。
Meal.java 
public class Meal {
   private List<Item> items = new ArrayList<Item>();    
 
   public void addItem(Item item){
      items.add(item);
   }
 
   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }        
      return cost;
   }
 
   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }    
}

步骤 6 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。

MealBuilder.java
public class MealBuilder {
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}


步骤 7 BuiderPatternDemo 使用 MealBuilder 来演示建造者模式(Builder Pattern)。

BuilderPatternDemo.java
public class BuilderPatternDemo {
   public static void main(String[] args) {
   
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

步骤 8
执行程序,输出结果:
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0

Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

原型模式

  • 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
  • 意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
  • 何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
  • 如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。
  • 关键代码: 1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone()  2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。
  • 优点: 1、性能提高。 2、逃避构造函数的约束。
  • 你可以克隆对象, 而无需与它们所属的具体类相耦合。
  • 你可以克隆预生成原型, 避免反复运行初始化代码。
  • 你可以更方便地生成复杂对象。
  • 你可以用继承以外的方式来处理复杂对象的不同配置。
  • 缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。克隆包含循环引用的复杂对象可能会非常麻烦。
  • 注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

实现

设计模式之创建型


设计模式之创建型

步骤 1 创建一个实现了 Cloneable 接口的抽象类。
Shape.java
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;
   }
}

步骤 2 创建扩展了上面抽象类的实体类。
Rectangle.java
public class Rectangle extends Shape {
   public Rectangle(){
     type = "Rectangle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

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

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

ShapeCache.java
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() {
      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);
 
      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(),rectangle);
   }
}

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

PrototypePatternDemo.java
public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();
 
      Shape clonedShape = (Shape) ShapeCache.getShape("2");
      System.out.println("Shape : " + clonedShape.getType());        
 
      Shape clonedShape2 = (Shape) ShapeCache.getShape("3");
      System.out.println("Shape : " + clonedShape2.getType());        
    }
}

参考文档