美文网首页
常用设计模式一(创建型模式)

常用设计模式一(创建型模式)

作者: 老实任 | 来源:发表于2017-07-29 20:54 被阅读0次

    前言

    很久前就一直想总结下常用的设计模式,但是各种原因一直拖到现在,下面我尝试用自己的理解去解释常用的设计模式,旨在让自己对常用设计模式有一个直观认识,过后回来能看懂并理解这些常用设计模式,当然如果这篇文章能帮到想了解设计的同学那就更好不过了。


    概念介绍

    设计模式分为三大类:

    • 创建型模式(Creational Pattern):创建型模式对类的实例化过程进行了抽象,将软件模块中对象的创建和对象的使用分离。使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不需要清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则
      创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
      设计模式中属于创建型模式的有:
      单例模式、建造者模式、工厂方法模式、抽象工厂模式、原型模式。
      其中常用的有:

      • 单例模式:保证一个类只有一个实例,并且提供对这个实例的全局访问方式。
      • 建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
      • 工厂方法模式:允许一个类的实例化推迟到子类中进行。
      • 抽象工厂模式:提供一个创建相关或依赖对象的接口,而不指定对象的具体类。

      这几个模式在这篇文章中都会有介绍。

    • 结构型模式(Structural Pattern):结构型模式描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。
      结构型模式可以分为类结构型模式和对象结构型模式:
      类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
      对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
      设计模式中属于结构型模式的有:适配器模式、代理模式、享元模式、外观模式、桥接模式、组合模式、装饰器模式。
      其中常用的有:

      • 适配器模式:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
      • 代理模式:给某一个对象提供一个代理,并由代理对象控制对原对象的引用,它是一种对象结构型模式。
      • 享元模式:运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。

    这几个模式会在下篇文章中介绍。

    • 行为型模式(Behavioral Pattern):行为型模式是对在不同的对象之间划分责任和算法的抽象化。行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象 之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。
      设计模式中属于行为型模式的有:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
      其中常用的有:
      • 策略模式:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。
      • 观察者模式:定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

    单例模式

    单例模式是最常用的设计模式之一,是为了保证单例对象的类只有一个实例存在,并保证整个系统只需要拥有一个全局对象。

    使用场景

    确保某个类在全局中只需要存在一个对象的场景。如:访问IO和数据库时,使用单例模式可以避免浪费资源。

    懒汉模式

    懒汉模式是声明一个静态变量,在调用对象类提供的get方法时就进行初始化,如下:

    public class LazySingleton {
        private static LazySingleton mSingleton;
    
        public static synchronized LazySingleton getLazySingleton() {
            if (mSingleton == null) {
                mSingleton = new LazySingleton();
            }
            return mSingleton;
        }
    }
    

    懒汉模式只会在使用时才会被实例化,可以节约一定的资源,但是每次调用getLazySingleton都会进行同步,造成不必要的同步开销,这种模式一般不建议使用。

    饿汉模式

    饿汉模式在类加载的时候就进行了初始化,所以类加载会稍慢,但是调用类实例时由于类已经初始化所以获取速度很快。如下:

    public class HungrySingleton {
        private static HungrySingleton mSingleton = new HungrySingleton();
    
        public static HungrySingleton getHungrySingleton() {
            return mSingleton;
        }
    }
    

    双重检查模式 (Double CheckLock)

    DCL方式的优点是能保证在需要是单例才会初始化的同时保证线程安全。如下:

    public class DCLSingleton {
    
        private static DCLSingleton mSingleton;
    
        public static DCLSingleton getDCLSingleton() {
            if (mSingleton == null) {
                synchronized (DCLSingleton.class) {
                    if (mSingleton == null) {
                        mSingleton = new DCLSingleton();
                    }
                }
            }
            return mSingleton;
        }
    }
    

    静态内部类单例模式

    这种方式不仅能确保线程安全也能保证Singleton类的唯一性,这是推荐使用的静态内部类单例模式。

    public class StaticSingleton {
        public static StaticSingleton getStaticSingleton() {
            return StaticSingletonHolder.singleton;
        }
    
        private static class StaticSingletonHolder {
            private static final StaticSingleton singleton = new StaticSingleton();
        }
    }
    

    小结

    以上四种是比较常用的单例写法,单例模式在日常开发中使用很多,在Android源码中也有使用。


    建造者模式

    建造者模式是一步步创建复杂对象的创建型模式,这个模式可以让用户在不知道内部构建细节的情况下,控制对象的构造流程。如汽车有车轮、方向盘、座椅还有各种小零件等,将这些部件组装成一辆汽车就是一个复杂的过程。在这个事例里汽车就是一个复杂的对象,而将汽车部件组装成汽车的过程就是复杂对象的构建流程。建造者模式将复杂对象的构建过程和部件进行了良好的解耦,将耦合度降到最低。

    使用场景

    • 相同方法不同执行顺序会产生不同的事件结果时。
    • 多个部件或者零件都可以装配到一个对象中,但是产生的 运行结果又不相同时。
    • 产品类是个复杂的对象,或产品类调用顺序不同就会产生不同的作用时。
    • 当初始化一个对象异常复杂时。

    例子

    使用建造者模式组装一台汽车,假定一辆汽车只需要有车轮、方向盘、座椅这三个部件。那么一个汽车类就有如下几个变量和组装方法:
    产品类

    public class Car {
    
        private String mWheel;//车轮
        private String mSteering;//方向盘
        private String mSeat;//座椅
    
        /**
         * 组装车轮
         *
         * @param mWheel
         */
        public void setmWheel(String mWheel) {
            this.mWheel = mWheel;
        }
    
        /**
         * 组装方向盘
         *
         * @param mSteering
         */
        public void setmSteering(String mSteering) {
            this.mSteering = mSteering;
        }
    
        /**
         * 组装座椅
         *
         * @param mSeat
         */
        public void setmSeat(String mSeat) {
            this.mSeat = mSeat;
        }
        
        @Override
        public String toString() {
            return "Car{" +
                    "mWheel='" + mWheel + '\'' +
                    ", mSteering='" + mSteering + '\'' +
                    ", mSeat='" + mSeat + '\'' +
                    '}';
        }
    }
    
    

    Builder类组装模板
    一个良好的汽车组装流程应该有一套组装模板,模板包含各种控件的组装方法,这里的组装模板类如下

    public abstract class Builder {
        public abstract void buildWheel(String wheel);
    
        public abstract void buildSteering(String settring);
    
        public abstract void buildSeat(String seat);
    
        public abstract Car createCar();
    }
    

    而要想组装一辆具体的车必须传承这套组装模板:

    public class SixSixSixCarBuilder extends Builder {
    
        private Car mCar = new Car();
    
        @Override
        public void buildWheel(String wheel) {
            mCar.setmWheel(wheel);
        }
    
        @Override
        public void buildSteering(String steering) {
            mCar.setmSteering(steering);
        }
    
        @Override
        public void buildSeat(String seat) {
            mCar.setmSeat(seat);
        }
    
        @Override
        public Car createCar() {
            return mCar;
        }
    }
    

    然后还需要一个指挥者(Director )去规范指挥组装流程:

    public class Director {
    
        Builder mBuild = null;
    
        public Director(Builder build) {
            this.mBuild = build;
        }
    
        public Car createCar(String wheel, String steering, String seat) {
            this.mBuild.buildWheel(wheel);
            this.mBuild.buildSteering(steering);
            this.mBuild.buildSeat(seat);
            return mBuild.createCar();
        }
    
    }
    

    这时候谁要造汽车只需要找到这个指挥者即可,测试如下

    public class Test{
        public static void main(string[] args){
        
            Builder builder=new SixSixSixCarBuilder();
            Director director=new Director(builder);
            director.createCar("米其林三星轮胎","真皮方向盘","真皮座椅");
    
            System.out.println("汽车信息:"+builder.createCar().toString());
        }
    }
    

    小结

    从上面例子得出建造者模式主要有以下几个结构构成:
    - Product: 产品类。
    - Builder:抽象Builder类,规范产品的组建,子类会继承这个类实现具体的建造流程。

    • ConcreteBulider: 上面Builder的实现类,实现抽象Builder类定义的所有方法,并且返回一个组建好的对象。
    • Director: 指挥者类,用于统一组装流程。

    实际开发中Director类经常是忽略的,直接用Builder来对对象进行组装,如下:

    public class Test{
        public static void main(string[] args){
            
            Builder builder=new SixSixSixCarBuilder();
            builder.buildWheel("米其林三星轮胎");
            builder.buildSteering("真皮方向盘");
            builder.buildSeat("真皮座椅");
          
            System.out.println("汽车信息:"+builder.createCar().toString());
        }
    }
    

    这个Builder一般写成链式调用,这时Builder类的setter方法需要返回自身,如下:

    public abstract class Builder {
        public abstract Builder buildWheel(String wheel);
    
        public abstract Builder buildSteering(String steering);
    
        public abstract Builder buildSeat(String seat);
    
        public abstract Car createCar();
    }
    
    public class SixSixSixCarBuilder extends Builder {
    
        private Car mCar = new Car();
    
        @Override
        public SixSixSixCarBuilder buildWheel(String wheel) {
            mCar.setmWheel(wheel);
            return this;
        }
    
        @Override
        public SixSixSixCarBuilder buildSteering(String steering) {
            mCar.setmSteering(steering);
            return this;
        }
    
        @Override
        public SixSixSixCarBuilder buildSeat(String seat) {
            mCar.setmSeat(seat);
            return this;
        }
    
        @Override
        public Car createCar() {
            return mCar;
        }
    }
    
    

    调用时:

    public class Test{
        public static void main(string[] args){
    
            Builder builder=new SixSixSixCarBuilder();
            builder.buildWheel("米其林三星轮胎")
                    .buildSteering("真皮方向盘")
                    .buildSeat("真皮座椅") 
                    .createCar();
                    
            System.out.println("汽车信息:" + builder.createCar().toString());
        }
    }
    

    工厂方法模式

    工厂方法模式是应用最广泛的模式,它定义一个用于创建对象的接口,让子类决定实例化哪个类。

    使用场景

    任何需要生成复杂对象的地方都可以使用工厂方法模式。

    例子

    还是组装汽车,具体组装k3、k5属于同一车型的两款车。由于是同一车型所以一条生产线足够。

    先明确汽车需要的功能,即汽车抽象基类:

    public abstract class QiYaCar {
    
        public abstract void drive();//汽车可以开
    
        public abstract void speedUp();//汽车可以加速
    }
    

    创建抽象生产线定义类:

    public abstract class QiYaFactory {
        
        /**
         * 
         * @param clzz 具体汽车型号
         * @return 具体的汽车
         */
        public abstract <T extends QiYaCar> T createQiYaCar(Class clzz);
    }
    

    创建具体的生产线:

    public class QiYaCreateFactory extends QiYaFactory{
        @Override
        public <T extends QiYaCar> T createQiYaCar(Class clzz) {
            QiYaCar car= null;
            try {
                car = (QiYaCar) Class.forName(clzz.getName()).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return (T) car;
        }
    }
    

    生产具体车型:

    public class QiYaK3Car extends QiYaCar{
        @Override
        public void drive() {
            System.out.println("k3启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("k3加速");
        }
    }
    
    public class QiYaK5Car extends QiYaCar{
        @Override
        public void drive() {
            System.out.println("k5启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("k5加速");
        }
    }
    

    把上面各个类组装起来就可以形成一条完整的流水线:

    public class Client{
        public static void main(string[] args){
    
            QiYaFactory factory = new QiYaCreateFactory();
            
            QiYaK3Car k3Car = factory.createQiYaCar(QiYaK3Car.class);
            k3Car.drive();
            k3Car.speedUp();
    
            QiYaK5Car k5Car = factory.createQiYaCar(QiYaK5Car.class);
            k5Car.drive();
            k5Car.speedUp();
        }
    }
    

    小结

    工厂方法模式是一个很好的设计模式,但是从上面例子也可以看出,工厂方法模式也有一定的缺点,比如当添加新的产品时就要编写一个新的产品类,同时还要引用抽象层,导致类的结构复杂化。是否使用工厂方法模式需要根据实际需求决定。


    抽象工厂模式

    抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

    使用场景

    一个对象族有相同约束时可以使用抽象工厂模式,如Android、ios都有拨号软件,这个软件属于软件的范畴,但是所在的操作系统不一样,这时候开业考虑使用抽象工厂模式来设计拨号软件。

    例子

    起亚和现代有两条生成线,一个中型车,一个SUV。抽象工厂模式来进行实现这两条生产线。

    首先是抽象产品类:

    public abstract class BetweenCar{
      public abstract void drive();//汽车可以开
    
      public abstract void speedUp();//汽车加速
      
    }
    
    public abstract class SUVCar{
       public abstract void drive();//汽车可以开
    
       public abstract void speedUp();//汽车加速
    }
    

    生产具体产品

    public class QiYaBetweenCar extends BetweenCar{
        @Override
        public void drive() {
            System.out.println("起亚中型车启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("起亚中型车加速");
        }
    }
    
    public class XianDaiBetweenCar extends BetweenCar{
        @Override
        public void drive() {
            System.out.println("现代中型车启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("现代中型车加速");
        }
    }
    
    public class QiYaSUVCar extends SUVCar {
        @Override
        public void drive() {
            System.out.println("起亚SUV启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("起亚SUV加速");
        }
    }
    
    public class XianDaiSUVCar extends SUVCar {
        @Override
        public void drive() {
            System.out.println("现代SUV启动");
        }
    
        @Override
        public void speedUp() {
            System.out.println("现代SUV加速");
        }
    }
    

    创建生产汽车的抽象工厂里面有两条生产线,即生产中型车和SUV

    public abstract class CarFactory {
        
        public abstract BetweenCar createBetweenCar();
    
        public abstract SUVCar createSUVCar();
    
    }
    

    定义具体的工厂

    public class QiYaCarFactory extends CarFactory{
        
        @Override
        public BetweenCar createBetweenCar() {
            return new QiYaBetweenCar();
        }
    
        @Override
        public SUVCar createSUVCar() {
            return new QiYaSUVCar();
        }
    }
    
    public class XianDaiCarFactory extends CarFactory {
        @Override
        public BetweenCar createBetweenCar() {
            return new XianDaiBetweenCar();
        }
    
        @Override
        public SUVCar createSUVCar() {
            return new XianDaiSUVCar();
        }
    }
    

    最后客户端调用

    public class Client{
        public static void main(string[] args){
    
            CarFactory qiYaCarFactory = new QiYaCarFactory();
    
            qiYaCarFactory.createBetweenCar().drive();
            qiYaCarFactory.createBetweenCar().speedUp();
    
            qiYaCarFactory.createSUVCar().drive();
            qiYaCarFactory.createSUVCar().speedUp();
    
            CarFactory xianDaiCarFactory = new XianDaiCarFactory();
    
            xianDaiCarFactory.createBetweenCar().drive();
            xianDaiCarFactory.createBetweenCar().speedUp();
    
            xianDaiCarFactory.createSUVCar().drive();
            xianDaiCarFactory.createSUVCar().speedUp();
        }
    }
    

    小结

    抽象工厂模式一个显著的优点是分离接口与实现,客户端使用抽象工厂来创建需要的对象,而客户端不需要知道具体实现是谁,客户端只是面向产品的接口编程,使其从具体的产品视线中解耦,在切换产品类时更加灵活。当然抽象工厂模式不容易扩展新的产品类,因为增加一个产品类就需要修改抽象工厂,则所有具体工厂类都要修改。

    相关文章

      网友评论

          本文标题:常用设计模式一(创建型模式)

          本文链接:https://www.haomeiwen.com/subject/fjonkxtx.html