什么是设计模式?设计模式怎么解决开发问题?以及在日常开发中如何检测自己是不是用错了?...

什么是设计模式

        Christopher Alexander说过:“每一个设计模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复的劳动”.尽管Alexander所指的是城市和建筑模式,但他的思想也同样与用于面向对象设计模式,只是在面向对象的解决方案里,我们用对象和接口代替了墙壁和门窗。两类模式的核 心都在于提供了相关问题的解决方案。--- 《设计模式:可复用面向对象软件的基础》

 

设计模式怎样解决设计问题

通过显式地指定一个类来创建对象  在创建对象时指定类名称使你受特定实现的约束而不是特定接口的约束。这会使未来的变化更复杂。要避免这种情况,应该间接地创建对象。设计模式:---Abstract Factory,Factory Method,Prototype。


对特殊操作的依赖 当你为请求指定一个特殊的操作时,完成该请求的方式就固定下来了。为避免把请求代码写死,你将可以在编译时刻或运行时刻很方便地改变响应请求的方法。设计模式:---Chain of Resposibility,Command。


对硬件和软件平台的依赖 外部的操作系统接口和应用编程接口(API)在不同的软硬件平台上是不同的。依赖于特定平台的软件将很难移植到其他平台上,甚至都很难跟上本地平台的更新。所以设计系统时限制其平台相关性就很重要了。设计模式:---Abstract Factory,Bridge。


对对象表示或实现的依赖 知道对象怎样表示、保存、定位或实现的客户在对象发生变化时可能也需要变化。对客户隐藏这些信息能阻止连锁变化。设计模式:---Abstract Factory,Bridge,Memento,Proxy


算法依赖 算法在开发和复用时常常被扩展、优化和替换。依赖于某个特定算法的对象在算法发生变化时不得不变化。因此有可能发生变化的算法应该被孤立起来。设计模式:---Builder,Iterator,Strategy,Template Method,Visitor


紧耦合 紧耦合的类很难独立地被复用,因为它们是互相依赖的。紧耦合产生单块的系统,要改变或删掉一个类,你必须理解和改变其他许多类。这样的系统是一个很难学习、移植和维护的密集体。 松散耦合提供了一个类本身被复用的可能性,并且系统更易于学习、移植、修改和扩展。设计模式使用抽象耦合和分层技术来提高系统的松散耦合性。设计模式:---Abstract Factory,Command,Facade,Mediator,Observer,Chain of Responsibility。


通过生成子类来扩充功能 通常很难通过定义子类来定制对象。每一个新类都有固定的视线开销(初始化、终止处理等)。定义子类还需要对父类有深入的了解。如,重定义一个操作可能需要重定义其他操作。一个被重定义的操作可能需要调用继承下来的操作。并且子类方法会导致类爆炸,因为即使对于一个简单的扩充,你也不得不引入许多新的子类。 一般的对象组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法。新的功能可以通过以新的方式组合已有对象,而不是通过定义已存在类的子类的方式加到应用中去。另一方面,过多使用对象组合会使设计难于理解。许多设计模式产生的设计中,你可以定义一个子类,且将它的实例和已存在实例进行组合来引入定制的功能。 设计模式:---Bridge,Chain of Responsibility,Composite,Decorator,Observer,Strategy。


不能方便地对类进行修改 有时你不得不改变一个难以修改的类。也许你需要源代码而又没有(对于商业类型库就有这种情况),或者可能对类的任何改变会要求修改许多已存在的其他子类。设计模式提供在这些情况下对类进行修改的方法。 设计模式:---Adapter,Decorator,Visitor。

设计模式的分类

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

 

设计模式的六大原则

总原则:开闭原则(Open Close Principle

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。

1、单一职责原则

  不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。

 

2、里氏替换原则(Liskov Substitution Principle

  里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。

 

3、依赖倒转原则(Dependence Inversion Principle

  这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

 

4、接口隔离原则(Interface Segregation Principle

  这个原则的意思是:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

 

5、迪米特法则(最少知道原则)(Demeter Principle

  就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

  最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

 

6、合成复用原则(Composite Reuse Principle

  原则是尽量首先使用合成/聚合的方式,而不是使用继承。

 

检测自己是不是用错了设计模式

设计模式应用检验手段一:

 

大部分设计模式,是让你在在面向对象的基础上尽量消除继承的手段。所以,如果你用了一些设计模式,减少了继承,那你八成用对了。如果你用了一大堆设计模式,然而继承却越来越频繁,那你100%用错了。

之所以说大部分,是因为个别设计模式(比如享元模式)是为了解决特殊场景特殊问题而生的。

 

设计模式应用检验手段二:

 

一个设计合理的系统,因为解耦充分,各个模块独立性强,所以单元测试应该是比较容易写的。如果你用了一大堆设计模式,却发现给你写的类编写单元测试用例非常困难,那你一定是用错了。

 

设计模式应用检验手段三:

 

多态的本质是运行期动态决定程序的分支走向,也就是“更好的if”,而设计模式,至少是《设计模式》那本书中提到的那些模式,基本上是基于多态的。所以如果你合理的利用设计模式,你设计出的代码应该有较少的if,如果你的代码越使用设计模式if越多,或者更直观地说,缩进越多,你一定犯了错误。

 

设计模式应用检验手段四:

 

有些模式是很容易用错的,比如visitor模式,其实是为了解决java不支持double dispatch而存在的,然而其逻辑很晦涩。所以当你还在怀疑自己是否用对了设计模式的时候,你不应该使用这样的模式。

 

设计模式应用检验手段五:

 

类继承总的来说只有1.5种正确的打开方式:

第一种叫做模板方法(template method),是设计模式之一。这种模式说的是基类在一个抽象的层面实现了公有方法a,a依赖私有方法b,而b的实现是子类的工作。这种模式下,子类实现的虚方法b是不应该被外界调用的。当然有一种极端情况,b即使a本身(比如react中的render),此时模板方法蜕化为普通的多态。

第二种叫做mixin,类a通过继承一个基类b,获得某种相对独立的能力。然而这其实是一种不太好的设计,尤其是在需要mixin多种能力的时候,更合理的方式其实是在a内部创建b的实例。只是在比较简单的场景中,我们可以用继承糊弄一下,所以只能算0.5种。

除此之外,使用类继承基本上可以认为是错误的设计。

由此我们可以推论,在es6流行导致运行时mixin写法在前端不流行之后,前端代码中出现了一些新的设计错误。

 

设计模式应用检验手段六:

 

设计模式是一回事,设计模式的实现是另一回事,这一点很重要。比如著名的观察者模式(observer),在java中有大量应用。然而我们常见的观察者模式样板代码之所以长那个样,是因为java中缺乏事件订阅系统。在c++/c#以及js中实现观察者模式,就没必要那样写。因为c++有std:function,c#有委托,而js函数干脆就是对象。

 

最后附上那个令人头大的图

 

 

参考资料:《设计模式:可复用面向对象软件的基础》

     《设计模式解决的8个问题场景你了解么》

     《二十三种设计模式全解析

     《如何正确使用设计模式》

     《设计模式解决的问题》

 

转载于:https://www.cnblogs.com/hupo-wey/p/11550290.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值