悟空模式-java设计模式
目前已定义的java设计模式细分下来有二十余种,这篇博客主要是想从大家所熟知的孙悟空入手,阐述各个设计模式的概念和优缺点,以及他们之间的联系。
在下面介绍的每个设计模式里,都会有与西游记相关的具体案例,主要是为了方便大家理解与记忆,否则使用一些枯燥的例子,很难让人印象深刻。
在很多设计模式相关的书籍与博客中都有与孙悟空相关的代码案例,在这里,会结合《西游记》原文,尽量使用更加贴切的事例进行描述,如果你觉得所举事例不太适合,或者有更好的想法,欢迎在评论区中指出。
按笔者的理解,设计模式的作用无非是让代码解耦合、易扩展、设计清晰以及体现封装,当真正理解了设计模式的理念之后,就可以达到随意组合使用设计模式,或者在不参照具体的设计模式模板的情况下又能体现设计模式内在理念的高度,希望这个博客对大家有所帮助。
下面是各个设计模式的详细链接(暂无实际跳转的会陆续更新):
悟空模式-java-适配器模式
悟空模式-java-装饰器模式
悟空模式-java-代理模式
悟空模式-java-外观模式
悟空模式-java-桥接模式
悟空模式-java-享元模式
悟空模式-java-策略模式
悟空模式-java-模板模式
悟空模式-java-观察者模式
悟空模式-java-迭代子模式
悟空模式-java-责任链模式
悟空模式-java-命令模式
悟空模式-java-访问者模式
悟空模式-java-状态模式
悟空模式-java-中介者模式
悟空模式-java-解释器模式
悟空模式-java-备忘录模式
悟空模式-java-不变模式
悟空模式-java-合成模式
这里介绍一下Java的六大基本设计原则:
1.单一职责原则
一个类只负责一件事情,尽量不要让一个类因为多种原因(业务变更)需要发生改变,也尽量不要让一个类变得太复杂。
比如孙悟空就负责打妖怪,沙僧就负责扛行李,白龙马就负责背着唐僧,这样是单一职责。要是让白龙马又负责背唐僧,又负责驮行李,还要负责打妖怪,白龙马就崩溃了,搞不好没几天就回鹰愁涧了。你可以把单一职责原则理解为白龙马只背唐僧原则。
2.里氏替换原则
任何父类可以出现的地方,子类也一定可以出现,将父类替换为子类对程序没有影响。子类可以实现父类的抽象方法,但不要重写父类的非抽象方法。子类可以增加自己新的行为,但不要覆盖父类已经实现的方法。当子类的方法重载父类的方法时,方法的前提条件(即方法的参数)要比父类方法的输入参数更宽松;当子类的方法实现父类的抽象方法时,方法的后续条件(即方法的返回值)要比父类更严格。因为当调用父类的方法时,用户只知道父类的前提条件和后续条件。
比如有一个父类叫唐僧徒弟,那么孙悟空可以继承这个类。系统中,所有用到唐僧徒弟类的地方,它的具体实现被替换为孙悟空或者猪八戒都不会影响到程序的正常执行。系统允许孙悟空和猪八戒有自己独特的行为,但父类唐僧徒弟中已经实现的方法,尽量不要去重写,否则可能会影响调用者的认知,因为调用者只知道获取的对象是唐僧徒弟,所以它只对预期中唐僧徒弟的行为做了处理,一旦方法被重写,可能会产生问题。比如唐僧对于徒弟,当徒弟不听话的时候,就调用“紧箍咒”方法,唐僧徒弟类会执行相应的“听到紧箍咒”方法,变得“听话”。可万一某个徒弟(肯定是孙悟空了)悄悄地把“听到紧箍咒”方法给重写了,就会导致代码不能按唐僧的预期正常运行,唐僧搞不清楚为什么对徒弟念紧箍咒,徒弟却还是不听话。你可以把里氏替换原则理解为紧箍咒必须有效原则。
3.依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程而不针对实现编程,面向抽象编程而不面向细节编程。
比如孙悟空有七十二变,时而变成鸟,时而变成鱼,有时又变成小和尚。如果我们依赖具体的实现进行编程的话,那么七十二变后孙悟空移动的方法就会写成下面这样:
public static void move(){ if(Bird){ fly; }else if(Fish){ swim; }else if(Human){ walk; }else if(...){ ... } }
这样会非常难以维护和调整,也非常难以阅读。但如果我们依赖抽象编程的话,可以把鸟、鱼、小和尚等等都抽象成孙悟空的一种变化,然后每个变化都实现一个move的方法,当我们调用孙悟空七十二变后的移动方法时,系统就会自动根据孙悟空当前的变化类型调用对应的move方法,这样就将依赖分离开了,上层代码只要依赖七十二变的抽象进行编程,而不需要依赖七十二变的具体实现形式。你可以把依赖倒置原则理解为七十二变原则。
4.接口隔离原则
一个类对另一个类的依赖应该建立在最小的接口上,客户端不应该依赖它不需要的接口。
这个原则理解起来可能很容易和单一职责原则产生混淆。我们要知道的是,单一职责原则针对的是程序的实现和细节,注重的是职责,而接口隔离原则注重的是隔离,针对的是整体框架的构建。
比如唐僧的几个徒弟各有各的特点,各有各的职责,如果让他们都依赖同一个接口,就会出现孙悟空也要实现背行李方法,白龙马也要实现打妖怪方法的情况,尽管孙悟空从来不背行李,也基本上从来轮不到白龙马打妖怪,这就造成接口非常臃肿。所以接口中的方法应该尽量少,我们可以将各个徒弟的职责抽象为战斗类、生活类等各个独立的接口,徒弟们只要实现自己必须要实现的接口就可以了,这样依赖多个专用的接口会比依赖一个综合的接口灵活的多。你可以把接口隔离原则理解为白龙马不打妖怪原则。
5.最少知道原则(迪米特法则)
一个类对自己依赖的类知道的越少越好。作为一个被依赖的类,应该尽可能将自己的实现细节保留在内部,除了提供必需的接口,不泄露任何不必要的信息。
比如孙悟空经常要打妖怪,但是唐僧总是干预孙悟空打妖怪的细节,非要说孙悟空打错了,这就是因为作为一个被依赖的类,孙悟空没有把自己的打妖怪逻辑严密地封装起来,导致依赖他的唐僧可以干预他执行打妖怪的方法,但实际上他并不需要唐僧告诉他怎么打妖怪。如果唐僧能够遵循最少知道原则,对自己不能完全了解的孙悟空打妖怪逻辑不进行干预的话,西游路上将会少去很多麻烦,因为你往往并不能充分了解自己依赖的类内部的实现逻辑,也往往无法做到正确的干预。你可以将最少知道原则理解为唐僧指挥悟空打妖怪原则。
6.开闭原则
一个软件实体,如类、模块、方法,应当对扩展开放,对修改关闭。通俗的说,就是尽量通过扩展实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
比如孙悟空从自由自在的齐天大圣变成了苦苦取经的孙行者,当我们要调整孙悟空的一些行为模式的时候,尽量不要修改齐天大圣的实现代码,可以考虑增加孙行者的接口,让孙悟空实现这个新的接口,然后增加孙行者的行为。这样能够防止一些旧的代码中使用了齐天大圣代码,修改后可能出现错误,也能够避免日后取经结束,孙悟空又变回了齐天大圣,又要修改回原来的代码,增加工作量不说,也不一定能够完全还原齐天大圣的全貌。你可以将开闭原则理解为齐天大圣孙行者原则。
正所谓无招胜有招,用不用、用哪个设计模式其实并不用刻意为之,只要能够深刻体现以上这些设计原则的精神,使得代码能够更方便更合理地适应业务变化的方向,你就会发现,自然而然就使用了一个或多个设计模式。