创建型设计模式包括:工厂模式、单例模式、建造者(生成器)模式、原型模式。
1、工厂模式(Factory)
1)简单工厂模式
工厂模式中会定义一个创建产品对象的工厂接口。如果要创建的产品类型不多(通常为一个或两三个),只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。简单工厂模式中创建实例的方法通常为静态方法,简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品。简单工厂模式的接口会代替对于产品对象构造函数的直接调用 (即使用 new运算符),不用担心, 对象仍将通过 new运算符创建, 只是该运算符改在工厂方法中调用罢了。只有一种产品类型的简单工厂示例可以参考《JAVA之反射》这篇文章中的动态代理部分。有两种产品的简单工厂示例如下:
class SimpleFactory { public static Product makeProduct(int kind) { switch (kind) { case PRODUCT_A: return new ProductA(); case PRODUCT_B: return new ProductB(); default: return null; } } }
2)工厂方法模式
如果以后要增加产品类型的话,对于简单工厂模式的话就需要修改创建产品实例的方法,使用“工厂方法模式”可以避免修改原来代码。工厂方法模式中的工厂类提供的是创建产品的虚方法或纯虚方法(Java的话即为一个接口),它返回的是产品基类类型。比如一个创建交通工具的工厂,接口返回交通工具类型,想要生产汽车的话就继承工厂类重写创建交通工具的接口,返回汽车类型对象,以后想要生产轮船的话就再增加一个从工程派生的类,然后重写接口:
interface AbstractFactory { public Product newProduct(); } class ConcreteAutoFactory implements AbstractFactory { public Product newProduct() { return new AutoProduct(); } } class ConcreteShipFactory implements AbstractFactory { public Product newProduct() { return new ShipProduct(); } }
3)抽象工厂模式
汽车和轮船属于同一基类交通工具,如果有不同类型的产品,比如电脑和手机需要生产的话,可以在“工厂方法模式”的基础上增加一个创建电子类产品的方法,这称为“抽象工厂模式”:
interface AbstractFactory { public TrafficProduct newTrafficProduct (); //创建交通类工具 public ElectProduct newElectProduct(); //创建电子类产品 }
2、单例模式(Singleton)
单例模式能够保证一个类只有一个实例,这样就可以控制某些共享资源 (例如数据库或文件) 的访问权限,避免对共享资源的多重占用,同时只创建一个对象也可以节省内存,避免频繁的创建和销毁对象,加快对象访问速度。例如日志对象一般为单例,数据库连接池也采用单例模式,这样可以节省打开或者关闭数据库连接所引起的效率损耗。
如下为Java中单例类的实现,可以看到我们并没有将getInstance()方法整体加锁,而是使用了双重检测机制,因为锁机制属于重量级操作,如果每次调用getInstance()都使用锁的话代价就很高,双重检测可以很好的避免这个问题。关于instance为何要使用volatile可以参考《Java线程2》这篇文章里对volatile的讲解。
class Singleton { private static volatile Singleton instance; //实例引用应为static volatile类型 private Singleton() {} //构造方法设置为私有 public static Singleton getInstance() { //双重检测机制避免每次获得实例都加锁 if(instance == null) { synchronized (Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }
3、建造者模式(Bulider,又称生成器模式)
构造方法有时候会有多个参数,但有时这些参数并不是必须的,比如下面的电脑类,cpu和ram是必须的,其它的硬件可以不设置或者部分设置,我们可能想到会使用默认参数或构造函数的重载来优化代码,如下所示,但是这种优化方法对于阅读或者使用者来说并不方便,我们可以使用建造者模式来构建代码。
Computer(String cpu, String ram, int usbCount, String keyboard, String display); //Computer("双核CPU", "8G内存", 0, "", ""); //Computer("双核CPU", "8G内存", 2, "", ""); //Computer("双核CPU", "8G内存", 2, "机械键盘", ""); //使用默认参数 Computer(String cpu, String ram, int usbCount = 0, String keyboard = "", String display = ""); //使用构造函数的重载 Computer(String cpu, String ram); Computer(String cpu, String ram, int usbCount); Computer(String cpu, String ram, int usbCount, String keyboard); Computer(String cpu, String ram, int usbCount, String keyboard, String display);
建造者模式将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。建造者模式的角色主要有产品、建造者、指挥者(又称管理者,可以没有)。当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
产品:包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
建造者:可以分为抽象建造者和具体建造者,抽象建造者包含创建产品各个部件的抽象方法,通常还包含一个返回复杂产品的方法 getResult(),具体建造者实现产品各个部件的具体创建方法。也可以只实现一个具体的建造者。
指挥者(管理者):将用于创建产品的一系列步骤调用抽取成为单独的方法,你的程序可以不需要指挥者。
class Builder; class Computer //产品 { public: Computer(std::string cpu, std::string ram) //只有CPU和内存是必须的 { this->cpu = cpu; this->ram = ram; } friend Builder; private: std::string cpu;//必须 std::string ram;//必须 int usbCount;//可选 std::string keyboard;//可选 std::string display;//可选 }; class Builder//建造者 { public: Builder(std::string cpu, std::string ram) { product = new Computer(cpu, ram); } Computer* getResult() { return product; } virtual void buildUSB(int usbCnt) { product->usbCount = usbCnt; } virtual void buildKeyboard(std::string strKeyboard) { product->keyboard = strKeyboard; } virtual void buildDisplay(std::string strDisplay) { product->display = strDisplay; } protected: Computer* product; }; class Director //指挥者(管理者) { public: Director(Builder* builder) { this->builder = builder; } Computer* construct() { builder->buildUSB(2); builder->buildDisplay("20寸显示器"); return builder->getResult(); } private: Builder* builder; }; int main() { //不使用指挥者,直接以特定顺序调用创建步骤 Builder* builder1 = new Builder("双核CPU", "2G内存"); builder1->buildUSB(2); builder1->buildDisplay("20寸显示器"); Computer* product1 = builder1->getResult(); //使用指挥者,适合程序中会反复以特定顺序来创建产品 Builder* builder2 = new Builder("双核CPU", "2G内存"); Director* director = new Director(builder2); Computer* product2 = director->construct(); }
生成器模式是 Java 中的一个著名模式。 当你需要创建一个可能有许多配置选项的对象时, 该模式会特别有用。StringBuilder/StringBuffer的append()、nio.ByteBuffer/CharBuffer/IntBuffer的put()等都使用了该模式。生成器模式可以通过类来识别, 它拥有一个构建方法和多个配置结果对象的方法,而且生成器方法通常支持方法链,如someBuilder->setValueA(1)->setValueB(2)->create()。下面是Java使用建造者模式的示例:
class Computer //产品 { private final String cpu;//必须 private final String ram;//必须 private final int usbCount;//可选 private final String keyboard;//可选 private final String display;//可选 public static class Builder //建造者 { private String cpu; private String ram; private int usbCount; private String keyboard; private String display; public Builder(String cup,String ram){ this.cpu=cup; this.ram=ram; } public Builder setUsbCount(int usbCount) { this.usbCount = usbCount; return this; } public Builder setKeyboard(String keyboard) { this.keyboard = keyboard; return this; } public Builder setDisplay(String display) { this.display = display; return this; } public Computer build() //生成产品 { return new Computer(this); } } private Computer(Builder builder) //产品类的构造方法设为私有,只允许通过建造者生成产品 { this.cpu=builder.cpu; this.ram=builder.ram; this.usbCount=builder.usbCount; this.keyboard=builder.keyboard; this.display=builder.display; } } public class Main { public static void main(String[] args){ //使用链式调用,一步一步的把产品对象构建出来 Computer computer=new Computer.Builder("因特尔","三星") .setDisplay("三星24寸") .setKeyboard("罗技") .setUsbCount(2) .build(); } }
4、原型模式(Prototype)
有的类对象的构建是很复杂或者耗时的,比如对象初始化中需要访问数据库获得数据,如果我们需要多次创建这样的对象的话就很耗时。再比如我们需要构建多个对象,这些对象都需要处于某种原始状态,那么就可以先构建一个拥有这种状态的对象,剩余的对象再通过该对象来复制获得。原型模式就是用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。原型模式包含抽象原型类和具体原型类,抽象原型类规定了具体原型类必须实现的接口,具体原型类里实现抽象原型类的 clone()/copy()方法,它是可被复制的对象。原型对象的克隆方式有深克隆(新对象中的成员变量不再指向原型里成员对象的地址,比如其是new一个新对象然后复制数据)和浅克隆(新对象中的成员变量指向与原型里成员对象的相同):
import java.util.ArrayList; import java.util.List; interface Copyable { //抽象原型类 Copyable copy(); } class Report implements Copyable { //具体原型类 private List<String> data; public Report() { this.data = new ArrayList<>(); } public Report(List<String> data) { this.data = data; } //耗时的数据加载操作 public void loadData() { data.clear(); //从数据库获得数据 String str1 = getDataFromMySQL(); String str2 = getDataFromOracle(); data.add(str1); data.add(str2); } public List<String> getContents() { return data; } @Override public Copyable copy() { List<String> cloneList = new ArrayList<>(data); //深拷贝,新对象的数据使用new出来的对象 //return new Report(data); //浅拷贝,与原型对象共享数据 return new Report(cloneList); } } public class Main { public static void main(String[] args){ //创建原型对象 Report reportPrototype = new Report(); //耗费资源的操作 reportPrototype.loadData(); //使用原型对象构建新的对象 Report reportWithTitle = (Report) reportPrototype.copy(); } }
Java中的Cloneable 接口就是抽象原型类(克隆方法为clone()),我们的类只要实现这个接口就实现了原型模式:
class Foo implements Cloneable { public Object clone() throws CloneNotSupportedException { return super.clone(); // 浅克隆,Object提供了浅克隆的 clone() 方法 } } public class Main { public static void main(String[] args)throws CloneNotSupportedException{ Foo obj1 = new Foo(); Foo obj2 = (Foo)obj1.clone(); System.out.println(obj1 == obj2); //false } }