设计模式--前言

设计模式的概念:

设计模式(Design Pattern)是一套被反复使用、多数人知晓、经过分类编目的优秀代码设计经验的总结。使用设计模式是为了提高代码的重用性,是代码更易理解并保证代码的可靠性。

这就是为什么之前用框架时有好多的疑问,如果懂了设计模式,就可以去剖析框架。也是追求写好代码的程序员们的必修课。

Java设计模式贯彻的原理是:面向接口编程,而不是面向实现。其目标原则是:降低耦合,增加灵活性。

设计模式的要素:

1.模式名称(pattern name)

  设计模式的名称简洁的描述了设计模式的问题、解决方案和效果。

2.问题(problem)

  问题描述了应该在何时使用模式。它解释了实际问题和问题存在的前因后果。

3.环境或初始环境(context或initial context)

  环境说明模式的使用范围,也是模式应用之前的起始条件。(也叫前提条件)

4.解决方案(solution)

  解决方案描述了设计的组成部分,以及他们之间的相互关系及各自的职责和写作方式。模式就是一个模板,可应用于多种不同场合,因此解决方案并不描述一个特定二具体的设计或实现。

5.效果(consequences)

  效果描述了模式应用和使用模式应权衡的问题。效果用来描述设计模式的利弊,它往往是衡量模式是否可用的重要因素。

6.举例(举例)

  通常举例使用一个或多个示意性的应用来说明特定的真实环境,并说明模式如何应用到环境中、改变环境并且给出当模式结束时的末态环境。

7.末态环境(resulting context)

  末态环境是模式应用到系统之后的状态。末态环境是模式的末态条件和可能有的副作用。她包括模式带来的好结果和坏结果。

8.推理(rationale)

  推理解释模式的步骤、规则,以及此模式作为一个整体是如何以特定的方式解决模式的。推理让使用者知道模式是如何工作的,为什么可以工作,以及使用此模式的有点是什么。

9.其他有关模式(related pattern)

  与他有关模式描述在现有的系统中某种模式与其他模式的静态和动态的关系。

10.已知应用(known uses)

  已知应用是指在一用的系统中出现或应用的模式例子,它有助于证明此模式确实是对一个重复发生的问题的可行性解答。

设计模式的原则:

用程序的复用可以提高应用程序的开发效率和质量,节约开发成本,恰当的复用还可以改善系统的可维护性。而在面向对象的设计里面,可维护性复用都是以面向对象设计原则为基础的,这些设计原则首先都是复用的原则,遵循这些设计原则可以有效地提高系统的复用性,同时提高系统的可维护性。 面向对象设计原则和设计模式也是对系统进行合理重构的指导方针。

 

1)单一职责原则(Single Responsibility Principle,SRP):类的职责要单一,不能将太多的职责放在一个类中。(高内聚、低耦合)

原则分析:
1、一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小,而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。 
2、类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。
3、单一职责原则是实现高内聚、低耦合的指导方针,在很多代码重构手法中都能找到它的存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。 

优点:
1、降低类的复杂性,类的职责清晰明确。比如数据职责和行为职责清晰明确。
2、提高类的可读性和维护性,
3、变更引起的风险减低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的类有影响,对其他接口无影响,这对系统的扩展性、维护性都有非常大的帮助。
注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否合理,但是“职责”和“变化原因”都是没有具体标准的,一个类到底要负责那些职责?这些职责怎么细化?细化后是否都要有一个接口或类?这些都需从实际的情况考虑。因项目而异,因环境而异。

2)开闭原则( Open - ClosedPrinciple ,OCP ):对扩展开放,对修改关闭

1、当软件实体因需求要变化时, 尽量通过扩展已有软件实体,可以提供新的行为,以满足对软件的新的需求,而不是修改已有的代码,使变化中的软件有一定的适应性和灵活性 。已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

2、实现开闭原则的关键就是抽象化 :在"开-闭"原则中,不允许修改的是抽象的类或者接口,允许扩展的是具体的实现类,抽象类和接口在"开-闭"原则中扮演着极其重要的角色..即要预知可能变化的需求.又预见所有可能已知的扩展..所以在这里"抽象化"是关键!

3、可变性的封闭原则:找到系统的可变因素,将它封装起来. 这是对"开-闭"原则最好的实现. 不要把你的可变因素放在多个类中,或者散落在程序的各个角落. 你应该将可变的因素,封套起来..并且切忌不要把所用的可变因素封套在一起. 最好的解决办法是,分块封套你的可变因素!避免超大类,超长类,超长方法的出现!!给你的程序增加艺术气息,将程序艺术化是我们的目标!
3)里氏代换原则( Liskov Substitution Principle ,LSP ):任何基类可以出现的地方,子类也可以出现

原则分析:
1、讲的是基类和子类的关系,只有这种关系存在时,里氏代换原则才存在。正方形是长方形是理解里氏代换原则的经典例子。
2、里氏代换原则可以通俗表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。把基类都替换成它的子类,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。
3、里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

优缺点:
在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:
1、代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
2、提高代码的重用性;
3、子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同;
4、提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;
5、提高产品或项目的开放性。

自然界的所有事物都是优点和缺点并存的,即使是鸡蛋,有时候也能挑出骨头来,继承的缺点如下:
1、继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
2、降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;
3、增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大片的代码需要重构。

4)依赖倒转原则( Dependence Inversion Principle ,DIP ):要依赖抽象,而不要依赖具体的实现.

定义:高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

1、抽象不应当依赖于细节;细节应当依赖于抽象;

2、要针对接口编程,不针对实现编程。

原则分析:

1、如果说开闭原则是面向对象设计的目标,依赖倒转原则是到达面向设计"开闭"原则的手段..如果要达到最好的"开闭"原则,就要尽量的遵守依赖倒转原则. 可以说依赖倒转原则是对"抽象化"的最好规范! 我个人感觉,依赖倒转原则也是里氏代换原则的补充..你理解了里氏代换原则,再来理解依赖倒转原则应该是很容易的。

2、依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。 

3、类之间的耦合:零耦合关系,具体耦合关系,抽象耦合关系。依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。

理解这个依赖倒置,首先我们需要明白依赖在面向对象设计的概念:
依赖关系(Dependency):是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。(假设A类的变化引起了B类的变化,则说名B类依赖于A类。)大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。

优点:采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。

5)合成/聚合复用原则(Composite/Aggregate ReusePrinciple ,CARP):要尽量使用对象组合,而不是继承关系达到软件复用的目的

定义:经常又叫做合成复用原则(Composite ReusePrinciple或CRP),尽量使用对象组合,而不是继承来达到复用的目的。

就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。简而言之,要尽量使用合成/聚合,尽量不要使用继承。

原则分析:

1、在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。
继承复用:实现简单,易于扩展。破坏系统的封装性;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用)
组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用)
2、组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。
3、此原则和里氏代换原则氏相辅相成的,两者都是具体实现"开-闭"原则的规范。违反这一原则,就无法实现"开-闭"原则,首先我们要明白合成和聚合的概念:
什么是合成?
合成(组合):表示一个整体与部分的关系,指一个依托整体而存在的关系(整体与部分不可以分开),例如:一个人对他的房子和家具,其中他的房子和家具是不能被共享的,因为那些东西都是他自己的。并且人没了,这个也关系就没了。这个例子就好像,乌鸡百凤丸这个产品,它是有乌鸡和上等药材合成而来的一样。也比如网络游戏中的武器装备合成一样,多种东西合并为一种超强的东西一样。
虽然组合表示的是一个整体与部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在,部分对象与整体对象之间具有同生共死的关系。
在组合关系中,成员类是整体类的一部分,而且整体类可以控制成员类的生命周期,即成员类的存在依赖于整体类。在UML中,组合关系用带实心菱形的直线表示。

什么是聚合?
聚合:聚合是比合成关系的一种更强的依赖关系,也表示整体与部分的关系(整体与部分可以分开),例如,一个奔驰S360汽车,对奔驰S360引擎,奔驰S360轮胎的关系..这些关系就是带有聚合性质的。因为奔驰S360引擎和奔驰S360轮胎他们只能被奔驰S360汽车所用,离开了奔驰S360汽车,它们就失去了存在的意义。在我们的设计中,这样的关系不应该频繁出现.这样会增大设计的耦合度。
在面向对象中的聚合:通常在定义一个整体类后,再去分析这个整体类的组成结构,从而找出一些成员类,该整体类和成员类之间就形成了聚合关系。在聚合关系中,成员类是整体类的一部分,即成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在。在UML中,聚合关系用带空心菱形的直线表示。 
6)迪米特法则(Law of Demeter,LoD):系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度

定义:又叫最少知识原则(Least Knowledge Principle或简写为LKP)几种形式定义:

1、不要和“陌生人”说话。英文定义为:Don't talk to strangers.

2、 只与你的直接朋友通信。英文定义为:Talk only to your immediatefriends.

3、 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

简单地说,也就是,一个对象应当对其它对象有尽可能少的了解。一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的一概不关心。

法则分析:

(1)朋友类:
在迪米特法则中,对于一个对象,其朋友包括以下几类:
(1) 当前对象本身(this);
(2) 以参数形式传入到当前对象方法中的对象;
(3) 当前对象的成员对象;
(4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;
(5) 当前对象所创建的对象。
任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。

(2)狭义法则和广义法则:
在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 

狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。
广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。
(3)迪米特法则的主要用途:在于控制信息的过载。
•在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;
•在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;
•在类的设计上,只要有可能,一个类型应当设计成不变类;
•在对其他类的引用上,一个对象对其他对象的引用应当降到最低。
7)接口隔离法则(Interface Segregation Principle,ISL):客户端不应该依赖那些它不需要的接口。(这个法则与迪米特法则是相通的)

定义:客户端不应该依赖那些它不需要的接口。
另一种定义方法:一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。
注意,在该定义中的接口指的是所定义的方法。例如外面调用某个类的public方法。这个方法对外就是接口。
原则分析:
1、接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
     •(1)一个接口就只代表一个角色,每个角色都有它特定的一个接口,此时这个原则可以叫做“角色隔离原则”。 
     •(2)接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。 
2、使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。
3、可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。

设计模式分类

通常的设计模式可以概括为23种,按照特点可以将其分为三大类型:创建型、结构型、行为型。

创建型

创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。所有的创建型模式都有两个主要功能:

  1.将系统所使用的具体类的信息封装起来

  2.隐藏类的实例是如何被创建和组织的。外界对于这些对象只知道他们共同的接口,而不清楚其具体的实现细节。

正因为以上两点,创建型模式在创建什么(what)、由谁来创建(who)、以及何时创建(when)这些方面,都为设计者提供了尽可能大的灵活性。

创建型模式的作用可以概括为:A.封装创建逻辑,不仅仅是new一个对象那么简单。B.封装创建逻辑变化,客户代码尽量不改变和尽量少修改。

创建型模式有以下几种:

  单例模式(Singleton Pattern):一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

  工厂方法模式(Factory Pattern):在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由其具体子类来完成。工厂的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中,它强调的是“单个对象”的变化。

  抽象工厂模式(Abstract Factory):抽象工厂是所有工厂模式中最为抽象且最具有一般性的一种形态。抽象工厂可以向客户提供一个接口,是的客户可以在没有指定产品的情况下,创建多个产品族中的产品对象,强调的是“对象系列”的变化。

  建造者模式(Builder Pattern):把构造对象实例的逻辑移到了类的内部,在类的外部定义了该类的构造逻辑。它把一个复杂对象的构造过程从对象的表示中分离出来,其直接效果是将一个复杂的对象简化为一个比较简单的目标对象,强调的是产品的构造过程。

  原型模式(Prototype Pattern):原型模式和工厂模式一样,同样对客户隐藏了对象创建工作具体的实现细节,但与通过对一个类进行实例化的构造心对象不同的是,原型模式通过复制一个现有的对象生成新对象。

其中工厂模式(Factory Pattern)属于类创建型,其他的则是对象创建型。区别在与一个类创建型使用继承改变被实例化的类,而对象创建型模式将实例化委托给另一个对象。

结构型

  结构型模式讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象实现新的功能(对象结构型模式)。这些结构型模式在某些方面具有很大的相似性,但侧重点各有不同。适配器(Adapter)是类结构型,其他的则为对象结构型模式。

  代理模式(Proxy):为其他对象提供一种代理以控制对该对象的访问。

  装饰模式(Decorator):动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更灵活。

  适配器模式(Adapter):将一个类的接口变换成客户端所期待的接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

  组合模式(Composite):也叫合成模式,将对象组合成属性结构一表示“部分-整体”的层次结构,使得用户对单个对象和组成对象的使用具有一致性。

  桥梁模式(Bridge):也叫桥接模式,将抽象和实现解耦,是的两者可以独立的变化。

  外观模式(Facade):也叫门面模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,外观模式提供一个高层次的接口,使得子系统更易于使用。

  亨元模式(Flyweight):是池技术的重要实现方式,使用共享对象可有效的支持大量的细粒度的对象。

行为型

  行为型设计模式关注的是对象的行为,用来解决对象之间的联系问题。行为类模式使用继承机制在类间分派行为(Interpreter、Template Method)。除Interpreter、Template Method都是行为对象型使用的复合而不是继承。

  模板方法模式(Template Method):定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以在不改变算法的结构的情况下重新定义该算法的某些特定步骤。

  命令模式(Command):是一种高内聚的模式,讲一个请求封装成一个对象,从而使用不同的请求吧客户端参数化。对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

  责任链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连城一条链,并沿着这条链传递该请求,直到有对象处理它为止。

  策略模式(Strategy):也叫政策模式,定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

  迭代器模式(Iterator):提供一种方法访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。

  中介者模式(Mediator):用一个中介对象封装一系列对象交互,中介者使各对象不需要显示相互作用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

  观察者模式(Observer):也叫发布订阅模式,定义对象间的一对多的依赖关系,是的每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

  备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态。

  访问者模式(Visitor):封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

  状态模式(State):当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类型,状态模式的核心是封装,状态的变更会引起行为的变更。

  解释器模式(Interpreter):给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该文法表示来解释语言中的句子。

转载于:https://www.cnblogs.com/hhxz/p/10791369.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值