zoukankan      html  css  js  c++  java
  • 创建型设计模式

     创建型设计模式包括:工厂模式、单例模式、建造者(生成器)模式、原型模式。

    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;
            }
        }
    }
    View Code

      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();
        }
    }
    View Code

      3)抽象工厂模式

      汽车和轮船属于同一基类交通工具,如果有不同类型的产品,比如电脑和手机需要生产的话,可以在“工厂方法模式”的基础上增加一个创建电子类产品的方法,这称为“抽象工厂模式”:

    interface AbstractFactory 
    {
        public TrafficProduct newTrafficProduct (); //创建交通类工具
        public ElectProduct newElectProduct(); //创建电子类产品
    }
    View Code

    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;
        }
    }
    View Code

     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);
    View Code

      建造者模式将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。建造者模式的角色主要有产品、建造者、指挥者(又称管理者,可以没有)。当一个类的构造函数参数个数超过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();
    }
    View Code

      生成器模式是 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();
        }
    }
    View Code

     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();
        }
    }
    View Code

      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
        }
    }
    View Code
  • 相关阅读:
    脚本 var 元素,集,方法
    Android——SharedPreferences存储(作业)
    Android——ListView相关作业(修改版)
    Android——AutoCompleteTextView、Spinner和消息提示
    Android——GridView(显示文字)
    Android——GridView
    Android——BaseAdapter相关
    Android——模拟文件拷贝
    Android——计算器
    Android——ListView
  • 原文地址:https://www.cnblogs.com/milanleon/p/14954666.html
Copyright © 2011-2022 走看看