设计模式的奥义

2018.12.07 设计模式的奥义

给这个文章起标题之前,我还犹豫了一下,我觉得奥义还是准确的,我自认为已经理解了设计模式的精髓。

我在最开始接触设计模式的时候还是很迷茫的,不知道怎么回事。并且当时以为设计模式能够解决我的问题,到现在为止1年多过去了。我对设计模式有了更深刻的理解,而且核心的理解没有大变化,而且我不认为之后会有大的变化.

当然,我的学习过程中还产生过与MVC设计模式纠缠的问题,不过现在已经搞清了,这些东西描述的层级是不一样的。就像说城镇指的是一个地方,说国家也指的是一个地方,但是理解的层次是不同的。设计模式指的是更细节的东西,而MVC这种指的是整个项目的。

不过坦诚讲,我觉得王垠说的是对的,这个四人帮提出的23种设计模式,唬人的成分更大。看起来高级的不行,实际上核心的思路就那么几条。有些人就是故意把一些很简单的东西说的很困难,同时也让别人觉得很困难。

一天有人跟你说,屎真香,尝一口吧。然后你忍不住,真的尝了一口,你俩相视一笑。,也开始见人就说屎真香。

到目前为止,在java中AbstructImplement的区别差距非常小,在大部分场景下都可以互相替换,区别多在于class的extend和 interface 的 implements。

函数式接口的大量补充,让interface 也具有了默认方法的功能,这两种东西差距更小了。所以我在文中会故意忽略这两种东西,因为没啥区别.而且我印象中查stof说的也是这个意思.

设计模式的核心

以下几条是我总结出来的设计模式的核心概念,不管设计模式说什么原则,什么概念,有这几个核心的技巧,就能理解所有的设计模式了.

最核心的内容就是接口,除此之外,还有几个技巧值得一说.然后就没什么可说的了。

接口

设计模式的核心就是接口,原本AB两个东西混在一起,现在通过一个接口C,把AB拆解开,让AC混在一起,B实现C。

C这个接口就相当于一个约束,规定了某些行为。满足行为的任何东西都可以。所以在A看来,任何满足条件C的东西,都可以。

有了这样的理解之后,设计模式的大部分东西都没有必要看了。因为绝大部分都是这个思想,然后起个高大上的名字。

这种接口概念的延伸就是用3号来把12拆开,让13和 23产生关系.就像中介者模式,就算没有用到接口,也实现了两者的分开.不过概念和接口是一样的,用第三者来应对改变.

2018.12.12补充,最近在搞博客的时候,CNAME也是这个配置思路.别人的ip被别人的域名隐藏起来了,然后我们改动的时候使用CNAME,如果没有CNAME这个第三者,别人IP改变的时候就麻烦多了.

技巧1

在接口或者是父类什么地方,规定调用几个内部的接口,一部分要求子类改,一部分阻止子类修改。这个技巧很有意思,在HashMap的源码中也有出现.

这个技巧的核心是自己调用自己的某些方法,被调用的某些方法可以被子类修改。注意这里说的是可以,这里面的抽象类是必须修改,在hashmap源码中也可以改成可选的。强制都必须要修改就是所谓的模版模式

public abstract class Game {
//子类需要重写的方法
   abstract void initialize();
   abstract void startPlay();

//子类不可改变的方法
   public final void play(){
      initialize();
      startPlay();
   }
}

技巧2——我帮你做

A类型 内部持有一个其他的对象B,具体怎么持有的无所谓,甭管是类初始化,还是什么时候放进去的。

然后就可以调用其他对象B的方法了。如果AB实现了相同的接口(当然要加A自己的私货了,完全一样有用么?),就叫做*装饰器*。如果实现的不同接口,叫做适配器,*策略模式*同理。(喷了,完全一样叫做*代理模式*,翻到代理模式才看到)

AB类型实现了相同接口,同时,内部有一堆B的话,叫做*组合*。或解释器(当然稍有差别)

这种包装的技巧有点意思。

技巧3—-调用反转

原本使用的时候是A对象,调用B对象.A.aa(B)

但是通过如下接口抽象,能让B.bb(A)实现一样的功能,做法就是把A改成接口,让真实的A来实现,在bb方法内部调用A.aa(this)这样就实现了调用的反转.

这就是访问者模式 的核心了.

状态模式 同上

这个技巧表面上看起来是翻转了调用,实际上,还要调用回来的.只不过人们都认为调用接口是解耦,就是这么回事.

技巧4—-观察者

观察者的核心其实也是上面这个技巧3,只不过做了一些扩展.这种反转控制的行为能造成一种通知的效果,观察的人会得到通知.

怎么得到呢?在通知的人身上进行注册,注册的技巧就是上面说的调用反转.所以最后就实现了,观察者调用一个方法,入参是 通知的人.

通知的人发一个消息,观察者的某些行为就会被触发.

在最开始学设计模式的时候,对这个印象非常深,现在看来就是一个 利用接口的调用反转.

技巧5—-内部类接口

在类中,写一个私有的内部类,让这个内部类实现一个接口,在类中提供一个方法返回这个内部类.

这就是内部类设计的真实含义

这个技巧可以不暴露内部的对象的同时,仅仅是获得一个接口实现.

内部类就是干这个用的.但是在设计模式中却变成了迭代器模式

命名

额外的一点好处就是命名了,老外起名字还是很准确的。一看到xxxFactory你就知道是工厂了,好处也就是这个了吧。

点评

下面分别对这些做一些点评,总感觉人家都提出来了23个,某些东西什么都不说也不好。

单例

全局惟一一个怎么办呢?

很简单,私有化构造器,不让别人轻易地初始化,自己提供一个静态方法,提供唯一的静态对象。

单例的操作确实是一种范式.

工厂

定义了商品的行为,抽出一样的接口。

抽象工厂

除了定义商品行为的接口,还定义了工厂的接口。你看,还是接口吧。

建造者

真是封装了一下复杂对象的初始化。连设计模式都算不上吧,这个东西标准设计模式还要要求你抽接口,实际上就看需求了。

原型

clone一下,有啥可说的?不就是复用半成品的对象而已。

适配器

见上文

桥接

就是把两个接口搞一起了,大家都用接口,随便换。

组合

这东西也叫做设计模式?那不就是随手写的东西么?这属于OOP基础了.

装饰器

这个上面说过了,抽象类写不写无所谓的。

外观模式

把一堆对象扔到一起,然后让别人调用。按照这个逻辑讲的话,绝大部分代码都是外观模式了。

享元模式

这个东西叫做 缓存技术更恰当吧,也是接口,统一相同的对象,缓存起来。

代理

上面说了。

访问者模式

把两个接口混合在一起互相调用,原本是a.xx(b),(a是参观者),(b是被调用的)但是改成了b.xxx(a)在b的内部为a.xx(this) 就反转了方法调用.

模板模式

说过了

策略模式

同上

状态模式

同上文

观察者模式

同上文

备忘录模式

就是使用一个类缓存了一下状态,这种也能称之为设计模式?

中介者模式

就是想办法找一个类,对接两个相同的类型.具体怎么操作都无所谓,反正结果就是,AB原本互相有关系,现在变成了只和中介有关系了.

迭代器模式

这个很常规,类库内部有实现. 这个技巧我不能把它说成是设计模式,我只能说是内部类的技巧。

解释器

大家都实现同一个解释规则,某些复杂规则可以通过简单的规则组合起来.本质上和装饰器适配器差不多.

命令模式

看成中介,或者是代理,完全没有任何问题,只不过执行的东西是一个对象,即要调用对象的某些方法才算是执行,而实际的执行方法已经写死到了接口实现中.没有本质区别

责任链模式

在接口中预设,其他的调用对象,实现类中,调用这个对象进行实现.即责任链,实现的效果就是一层套一层,可以自己手动制定调用多少层. 反正都是接口嘛.

备注与总结

这里会说一些额外的点,一些常用的误会.也顺便做一些总结吧.

我对设计模式的理解就是这样了,核心的内容就是那么几个,

而且我印象中好像还有什么,迪米特里原则,里氏替换原则,依赖倒置什么的各种原则,这些原则都不重要.

只要是编码水平正常,封装多态什么的都不是设计模式要考虑的问题.以上的原则都不重要.

还有一点想提的就是,《java编程思想》这本书比普通编程入门书难读的原因就是,因为里面融合了很多设计模式的东西。这本书还套了很多的概念和模型。理解起来确实比普通的数费力一些。

  1. java的代理和代理模式不是一个东西,java的代理是一种实现,一种具体的做法,这个做法没有用到代理的设计模式。
  2. MVC、MVVM和这里面说的设计模式不是一个事情。上文已经解释了.
  3. 很多代码都可以套上设计模式,但实际上使用者并没有 有意识的用这些模式,实质上模式不是用来套的,这种设计模式是总结出来的产物.
  4. mybatis源码中,经常需要调用xxx.builder() 来初始化对象。这个东西和标准的建造者设计模式还不一样,不好混为一谈。
  5. 实际工作当中最常见的设计模式就有几个,大部分都不是很常用
  6. 很多设计模式是非常相似的,区别不是很明显,强行说xxx是某某设计模式是合适的,而且设计模式之间不应该有对比,即便他们都是设计模式。