分类 设计模式 下的文章

建造者,开源框架中感觉应用非常常见的一种设计模式。常见的例如okhttp、retrofit,其中的配置都是通过Builder去添加的。
所以这是一种很方便的设计模式。
按照我们之前的模板设计方法举得例子。如果我作为一个消费者,通过模板方法去买外星人电脑,那么我会知道,这台电脑是如何组装的,是怎么拼接cpu,怎么拼接键盘,怎么拼接外壳。而且顺序也定死了。但是,如果是建造者模式的话,那么就会发现,其实我作为消费者,我不关心,如果制造的,我只是输入一些cpu型号,键盘样式,外壳材质就可以得到一台笔记本。我可以通过一个导演(这里指类似可以有限度组装笔记本的那种厂商---准系统)去实现制造一台笔记本(其实这里举例子不太好,应该相应的改为金拱门之类的好点,你作为顾客去点餐,不管是要巨无霸还是麦吉士,你可以再有限范围内挑选是否要黄瓜,是否要cheese等操作,而不需要知道汉堡如何制作出来,等着吃就是了。)那么建造者模式是如何实现的呢?

先来看看uml图
请输入图片描述

作为建造者模式,最重要的就是封装下层细节,有些繁琐的,无需着重实现的实现。
优点:封装性良好,无需关心每一个汉堡是如何生成的,最终生成相应的汉堡吃就完事了;建造者相对独立,容易扩展,需要啥汉堡流程,再添加一个就是了;便于控制细节风险(我的理解就是独立的一个建造模块,是可以自己把控的,不会对别的建造者产生问题和影响)。
使用场景:
产品类很复杂,但是调用顺序不同产生不同的东西,采取使用建造者模式比较适合。

他跟工厂模式有什么区别呢?建造者模式关注的是零件类型和装配工艺(顺序)。翻译过来就是说,工厂模式注重的还是产品本身,他所生产出来的产品都应该是类似的,好比工厂模式生产出来的笔记本,更加注重笔记本本身,有可能你是啥啥笔记本,但是你们功能没啥区别。建造者模式如果生产笔记本,那么他更加关心产品本身创造出来的过程。但是对于最终的消费者来说,他们获取其实没啥区别。

模板模式其实我们经常用,但是因为代码层面看起来很简单,我们不知道这是模板模式(是的,没读设计模式之前,我不知道-_-!)。

模板方法模式的定义:

定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义。

举个例子,笔记本蓝天模具场是个挺厉害的OEM厂。很多具体牌子的厂商会去买他的模具(自研开模比较贵),例如神舟,炫龙,外星人等等。但这些牌子有响当当的,也有传说中的小米都干不过的(神船)。所以他们需要在具体的笔记本配置上增删一些东西,增加自己的品牌和利润等等。蓝天就相当于模板,是抽象的;笔记本壳子,笔记本键盘,主板配置是各大厂商自己去配置的,是具体的笔记本了。

图例如下:
请输入图片描述

其中只是简单的运用了继承机制,但却也十分广泛运用。所以引申出来一个抽象模板。
其中分为两种方法:模板方法(一般final修饰,防止篡改) 和 基本方法(子类实现)。

用代码展现的话如下

/**
 * OEM工厂 这里工厂不是工厂模式的工厂只是厂商的意思,这里是OEM提供的模板
 */
public abstract class OEMFactory {
    protected abstract void addCPU();

    protected abstract void addKeyBoard();

    protected abstract void addShell();

    protected void makeLaplop(){
        this.addCPU();
        this.addKeyBoard();
        this.addShell();
    }


}

先写工厂模式之前,说说一个有趣的观点。曾经彩笔的我,刚踏上程序员之路的时候,只会单例、工厂模式。某次,在和某人讨论的时候,某人直接说工厂模式不需要,存在是没意义的,于是这段话就在我幼小的心灵中种下了一颗种子(主要是当时懒,觉得反驳不来,于是忘记了)。最近重新的梳理的时候,又想起了这个事,于是搜索了下,发现b乎上有大牛总结的很好,直接拖过来。

JAVA工厂方法的疑问(为何要创建工厂类或者工厂接口)? - 胖胖的回答 - 知乎

大意就是工厂模式的关键是模式,属于创建者模式。而创造者模式,最大的特征就是将“定义过程”和“实例化过程”分离。如果只是简单的用传参来重载方法完成,一旦功能变得复杂,代码重复率就会越来越多,代码也会庞大。但是换成工厂模式就不会。

参考设计模式之禅的工厂模式,女娲造人造黑种人,白种人,黄种人。(ps:原书作者写的很有意思,烤焦了变黑人,烤白了,经验不足,黄种人,正好完美,2333333)。用八卦炉去造人,下达的命令不是我要去造一个具体细节的人种(例如跑得飞快,头发颜色焦炭的黑人),而是直接说命令早黑种人,就会生成一个黑种人。
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

通用工厂类图

工厂模式的优点:

  • 良好的封装性,代码结构清晰。一个对象的创建是有条件约束的,比如一个调用者需要一个小白车,只需要知道小白车的图纸的名称,不用知道小白车是如何将轮子,将油漆等等创建出来的,降低模块的耦合性。
  • 扩展性非常优秀,需要需要小黄车,只需要继承汽车产品,再去再添加一个小黄车的具体产品的实现方案就行了。
  • 屏蔽产品类。这一特点非常重要。比如android中下载一张图片,我不管你是用httpUrlConnection还是okhttp去实现。这些细节对我不重要,我只需要知道你能给我下载图片就行了。具体用哪个看情况就是了,而且切换无需修改实现代码。
  • 工厂模式是典型的解耦框架,高层模块只需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要就不用交流,也符合依赖倒置原则,只依赖产品类的抽象;当然符合里氏替换原则原则,使用产品子类替换父类。

工厂模式的扩展:

  1. 简单工厂模式
    一个模块只需要一个工厂类,直接去除抽象的工厂,并且不需要产生,直接静态的方法就可以实现了。

优点,比标准版调用简单。
缺点,工厂类的扩展就比较困难了。

  1. 多个工厂模式
    一个产品对应一个工厂类,每个工厂类就负责创建对应的产品对象,非常符合单一职责原则。

优点,创建类职责清晰,结构简单。
缺点,可扩展性和可维护性破坏了,每次扩展都需要创建一个产品类,就需要一个工厂类,增加扩展的复杂度。


扩展

  1. 替代单例模式
    通过工厂模式在内存中生产一个对象。通过反射获取当前不允许new 对象的类的对象,获取其中的方法。然后设置访问权限,但是要注意保存内存中的对象唯一,不能多次反射获取。
  2. 延迟初始化
    一个对象消费完毕后,并不立刻释放,工厂类保持初始状态,等待再此使用。用map缓存创建好的工厂,如果下次再调用,查询map中是否拥有,有就直接取出生产产品。

多用于限制一个产品类的最大实例化数量,或者对象初始化比较复杂的情况下,通过延迟加载降低对象的产生和销毁带来的复杂性。

参考书籍
设计模式之禅

单例模式

一般分为 饿汉式 懒汉式 线程不安全的 线程安全的 以及内部类形式的单例。

为什么使用单例?好处呢?

因为单例在内存中只有一个实例,减少内存的开销。主要一般适用于一个对象需要频繁的创建、销毁,而且无法优化创建和销毁时的性能。避免对资源的多重占用。可以全局调用。

缺点?

  • 单例模式一般没有接口,扩展很困难,基本都是修改代码实现。
  • 与单一指责原则冲突。
  • 对测试不利
  1. 饿汉式 一步到位 类初始化时,就实例化instance

    private static SingletonDesign instance = new SingletonDesign();

    private SingletonDesign(){

    }
    public static SingletonDesign getInstance(){
        return instance;
    }
  1. 懒汉式 (线程不安全的)为什么不安全,是因为如果多个线程同时创建单例,则会失效,可能创建多个instance。
private static SingletonDesign instance = null;

    private SingletonDesign(){

    }
    public static SingletonDesign getInstance(){
        if (instance == null){
            instance = new SingletonDesign();
        }
        return instance;
    }

3 懒汉式(线程安全的)但是因为加了synchronized 所以效率低。

private static SingletonDesign instance = null;

    private SingletonDesign(){

    }

    public synchronized static SingletonDesign getInstance(){
        if (instance == null){
            instance = new SingletonDesign();
        }
        return instance;
    }

4 静态内部类 比饿汉式效率稍微高点,因为这种开始并不一定会获取instance,而是去调用getInstance时候才去实例化instance

private static class Singletonholder{
        private static final SingletonDesign instance= new SingletonDesign();


    }
    private SingletonDesign(){

    }
    public static final SingletonDesign getInstance(){
        return Singletonholder.instance;
    }

5 双重锁单例 懒汉线程安全版的升级

private volatile static SingletonDesign instance = null;

    private SingletonDesign(){

    }

    public  static SingletonDesign getInstance(){
        if (instance == null){
            synchronized (SingletonDesign.class){
                if (instance == null){
                    instance = new SingletonDesign();
                }
            }

        }
        return instance;
    }

设计模式之禅

常见的几种单例模式