《2022年设计模式六大原则归类 .pdf》由会员分享,可在线阅读,更多相关《2022年设计模式六大原则归类 .pdf(15页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、设计模式体现的是软件设计的思想,而不是软件技术,它重在使用接口与抽象类来解决各种问题。在使用这些设计模式时,应该首先遵守如表10-1所示的六大原则。表 10-1 设计模式六大原则原 则含 义具 体 方 法开闭原则对扩展开放,对修改关闭多使用抽象类和接口里氏代换原则基类可以被子类替换使用抽象类继承,不使用具体类继承合成复用原则要依赖于抽象,不要依赖于具体针对接口编程,不针对实现编程接口隔离原则使用多个隔离的接口,比使用单个接口好建立最小的接口迪米特法则一个软件实体应当尽可能少地与其他实体发生相互作用通过中间类建立联系依赖倒转原则尽量使用合成/聚合,而不是使用继承尽量使用合成/聚合,而不是使用继承
2、下面我们通过简单的实例来讲解这六大原则的具体含义。通常,对于开发完的代码都需要多种测试才能够投入使用,这包括:首先要经过开发人员的单元测试、集成测试。然后再到测试人员的白盒测试、黑盒测试。最后还要由用户进行一定的测试。经过漫长的测试,代码才能够投入使用。但是软件产品的维护和升级又是一个永恒的话题,在维护的过程中,你可能要不断地增加一些小功能;在升级的过程中,你要增加一些较大的功能。因此,软件产品随时都有扩展功能的要求。这种功能的扩展,就要求我们改变原有的代码。但是,对原代码的修改就会深刻地影响到原来的功能的方方面面:可能对旧代码引入了新的错误,使你不得不对旧代码进行大规模的修改。可能引起你不得
3、不重新构造系统的架构。即使新增的代码对旧代码没有影响,你也不得不对原来的系统做一个全面的测试。所有上述列出来的问题,都是对系统功能进行扩展所不能承受的代价。换句话说,我们设计出来的系统,一定要是扩展性良好的系统。如何才能够设计出扩展性良好的系统呢?这就需要在软件系统设计时遵守开闭原则:名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 15 页 -软件系统必须对扩展开放,对修改关闭。换句话说,我们的系统必须是可扩展的系统,而不是可修改的系统。做到开闭原则,就注意以下两点。1)多使用抽象类在设计类时,对于拥有共同功能的相似类进行抽象化处理,将公用的功能部分放到抽象类中,所有的操作都调用
4、子类。这样,在需要对系统进行功能扩展时,只需要依据抽象类实现新的子类即可。如图10-1所示,在扩展子类时,不仅可以拥有抽象类的共有属性和共有函数,还可以拥有自定义的属性和函数。2)多使用接口与抽象类不同,接口只定义子类应该实现的接口函数,而不实现公有的功能。在现在大多数的软件开发中,都会为实现类定义接口,这样在扩展子类时实现该接口。如果要改换原有的实现,只需要改换一个实现类即可。如图 10-2所示,各子类由接口类定义了接口函数,只需要在不同的子类中编写不同的实现即可,当然也可以实现自有的函数。名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 15 页 -以上两点将会在后续的各个设计
5、模式中得到充分的体现。名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 15 页 -里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的OOPSLA 大会上发表的一篇文章Data Abstraction and Hierarchy里面提出来的,主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中的蕴涵的原理。2002年,软件工程大师Robert C.Martin,出版了一本Agile Software Development Principles Patterns and Practices,在文中他把里氏代
6、换原则最终简化为一句话:Subtypes must be substitutable for their base types,也就是说,子类必须能够替换成它们的基类。我们把里氏代换原则解释得更完整一些:在一个软件系统中,子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。子类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充,它讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在。正方形是长方形是一个理解里氏代换原则的最经典的例子。在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,应该让正方形继承自长方形。长方形类如程序1
7、0-1所示。程序 10-1 长方形类 Rectangle.java 名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 15 页 -view plaincopy to clipboardprint?01.package principle.liskovsubstitution;public class Rectangle private int height;private int width;public int getHeight()return height;public void setHeight(int height)this.height=height;public i
8、nt getWidth()return width;public void setWidth(int width)this.width=width;继承了长方形的正方形类如程序10-2所示。程序 10-2 正方形类Square.java view plaincopy to clipboardprint?01.package principle.liskovsubstitution;public class Square extends Rectangle public void setWidth(int width)super.setWidth(width);super.setHeight(w
9、idth);public void setHeight(int height)super.setWidth(height);super.setHeight(height);由于正方形的长度和宽度必须相等,所以在方法setLength()和 setWidth()中,对长度和名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 15 页 -宽度赋值相同。程序10-3所示的测试类中的函数zoom()用来增加长方形的长和宽。程序 10-3 测试类 TestRectangle.java view plaincopy to clipboardprint?01.package principle.l
10、iskovsubstitution;public class TestRectangle public void zoom(Rectangle rectangle,int width,int height)rectangle.setWidth(rectangle.getWidth()+width);rectangle.setHeight(rectangle.getHeight()+height);显然,当增加的长度和宽度不同时,不能够将其中的长方形换成其子类正方形。这就违反了里氏代换原则。为了符合里氏代换原则,我们可以为长方形和正方形创建一个父类Base,并在其中定义好共有的属性,并定义一个z
11、oom()抽象函数,如程序10-4所示。程序 10-4 父类 Base.java view plaincopy to clipboardprint?名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 15 页 -01.package principle.liskovsubstitution;public abstract class Base private int height;private int width;public int getHeight()return height;public void setHeight(int height)this.height=heig
12、ht;public int getWidth()return width;public void setWidth(int width)this.width=width;public abstract void zoom(int width,int height);长方形类继承自该父类,并编写自己的zoom()实现函数,如程序10-5所示。程序 10-5 修改后的长方形类BaseRectangle.java view plaincopy to clipboardprint?01.package principle.liskovsubstitution;public class BaseRect
13、angle extends Base public void zoom(int width,int height)setWidth(getWidth()+width);setHeight(getHeight()+height);正方形类也继承自该父类,并编写自己的zoom()实现函数,如程序10-6所示。程序 10-6 修改后的正方形类BaseSquare.java 名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 15 页 -view plaincopy to clipboardprint?01.package principle.liskovsubstitution;publi
14、c class BaseSquare extends Base public void setWidth(int width)super.setWidth(width);super.setHeight(width);public void setHeight(int height)super.setWidth(height);super.setHeight(height);public void zoom(int width,int height)int length=(width+height)/2;setWidth(getWidth()+length);setHeight(getHeigh
15、t()+length);编写测试函数如程序10-7所示。程序 10-7 修改后的测试类BastTest.java view plaincopy to clipboardprint?01.package principle.liskovsubstitution;public class BastTest public void zoom(Base base,int width,int height)base.zoom(width,height);名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 15 页 -此时的 Base 类可以被它的子类Rectangle和 Square所替代,而
16、不用改变测试代码。这就是符合里氏代换原则的编写方式。由此可见,在进行设计的时候,我们尽量从抽象类继承,而不是从具体类继承。如果从继承等级树来看,所有叶子节点应当是具体类,而所有的树枝节点应当是抽象类或者接口。当然这只是一个一般性的指导原则,使用的时候还要具体情况具体分析。开闭原则的主要机制就是依赖倒转原则,这个原则的内容是:要依赖于抽象,不要依赖于具体,即要针对接口编程,不针对实现编程。依赖也就是耦合,共分为下面3 种。零耦合(Nil Coupling)关系:两个类没有依赖关系。具体耦合(Concrete Coupling)关系:两个具体的类之间有依赖关系,如果一个具体类直接引用另外一个具体类
17、,就是这种关系。抽象耦合(Abstract Coupling)关系:这种关系发生在一个具体类和一个抽象类之间,这样就使必须发生关系的类之间保持最大的灵活性。依赖倒转原则要求客户端依赖于抽象耦合,抽象不应当依赖于细节,细节应当依赖于抽象。这个原则的另外一个表述就是:要针对接口编程,不要对实现编程。程序在需要引用一个对象时,应当尽可能地使用抽象类型作为变量的静态类型,这就是针对接口编程的含义。依赖倒转原则是达到开闭原则的途径。要做到依赖倒转原则,使用抽象方式耦合是关键。由于一个抽象耦合总要涉及具体类从抽象类继承,并且需要保证在任何引用到某类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则
18、的基础,依赖倒转原则是OOD 的核心原则,设计模式的研究和应用都是用它作为指导原则的。再拿上一节的正方形和长方形为例,在最后的测试函数中,正确的方式是使用抽象类作为函数参数:view plaincopy to clipboardprint?1.public class BastTest public void zoom(Base base,int width,int height)base.zoom(width,height);名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 15 页 -2.即针对抽象类编程,如果将它换成如下的针对具体类的操作:view plaincopy to
19、clipboardprint?1.public class BastTest public void zoom(Rectangle rectangle,int width,int height)rectangle.zoom(width,height);这样该类就违反了依赖倒转原则,就不能够复用做正方形的操作了。依赖倒转原则虽然强大,但是也很难实现。另外,依赖倒转原则是假定所有的具体类都会变化,这也不是全对,有些具体类就相当稳定。使用这个类的客户端就完全可以依赖这个具体类,而不用再弄一个抽象类。接口隔离原则的意思是:使用多个隔离的接口,比使用单个接口好。也就是说,一个类对另外一个类的依赖性应当是
20、建立在最小的接口上的。在我们进行设计的时候,一个重要的工作就是恰当地划分角色和角色对应的接口。因此,这里的接口往往有两种不同的含义。1接口对应的角色指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象,接口的划分就直接带来类型的划分。这里,我们可以把接口理解成角色,一个接口只是代表一个角色,每个角色都有它特定的一个接口,这里的这个原则可以叫做角色隔离原则。例如,我们将电脑的所有功能角色集合为一起,构建了一个接口,如图10-3所示。名师资料总结-精品资料欢迎下载-名师精心整理-第 10 页,共 15 页 -此时,我的电脑和你的电脑要实现该接口,就必须实现所有的接口函数,显然接口混乱,并不能够
21、满足实际的需求:我的电脑可能是用来工作和学习的,你的电脑可能是用来看电影、上网和打游戏等娱乐活动的,那我们就可以将电脑的角色划分为两类,如图10-4所示。2角色对应的接口名师资料总结-精品资料欢迎下载-名师精心整理-第 11 页,共 15 页 -指某种语言具体的接口定义,有严格的定义和结构。比如Java 语言里面的Interface结构。对不同的客户端,同一个角色提供宽窄不同的接口,也就是定制服务,仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来。对于图 10-4中的接口定义,如果我的电脑除了工作和学习之外,还想上网,那就没办法了,必须实现娱乐电脑的接口,这样就必须实现它的所有接口函数了
22、。此时我们需要将对应角色中的接口再进行划分,如图10-5所示。这样,经过以上的划分,如果我的电脑想增加某一项功能,只需要继承不同的接口类即可。由此可见,对接口角色的划分,是从大的类上进行划分的;对角色的接口进行的划分,是对类的接口函数的划分。它们两者由粗到细,实现了接口的完全分离。迪米特法则(Law of Demeter)又叫最少知道原则(Least Knowledge Principle),1987年秋天由美国Northeastern University的 Ian Holland提出,被UML 的创始者之一 Booch等普及。后来,因为在经典著作The Pragmatic Programm
23、er中提出而广为人知。迪米特法则可以简单说成:talk only to your immediate friends。对于面向OOD来说,又被解释为下面几种方式:一个软件实体应当尽可能少地与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。名师资料总结-精品资料欢迎下载-名师精心整理-第 12 页,共 15 页 -迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类直接建立直接的接触。如果真的有需要建立联系,也希望能通过它的
24、友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系-这在一定程度上增加了系统的复杂度。例如,购房者要购买楼盘A、B、C 中的楼,他不必直接到楼盘去买楼,而是可以通过一个售楼处去了解情况,这样就减少了购房者与楼盘之间的耦合,如图10-6所示。后文中的外观模式(Facade)和中介者模式(Mediator),都是如上这种迪米特法则应用的例子。合成(Composition)和聚合(Aggregation)都是关联(Association)的特殊种类。聚合表示整体和部分的关系,表示拥有;合成则是一种更强的拥有,部分和整
25、体的生命周期一样。合成的新的对象完全支配其组成部分,包括它们的创建和销毁等。一个合成关系的成分对象是不能与另一个合成关系共享的。在面向对象设计中,有两种基本的办法可以实现复用:第一种是通过合成/聚合,即合成复用原则,含义是指,尽量使用合成/聚合,而不是使用继承。第二种就是通过继承。要正确地选择合成/复用和继承的方法是,只有当以下的条件全部被满足时,才应当使用继承关系:名师资料总结-精品资料欢迎下载-名师精心整理-第 13 页,共 15 页 -子类是超类的一个特殊种类,而不是超类的一个角色,也就是区分Has-A和Is-A。只有 Is-A关系才符合继承关系,Has-A关系应当用聚合来描述。永远不会
26、出现需要将子类换成另外一个类的子类的情况。如果不能肯定将来是否会变成另外一个子类的话,就不要使用继承。子类具有扩展超类的责任,而不是具有置换掉(override)或注销掉(Nullify)超类的责任。如果一个子类需要大量的置换掉超类的行为,那么这个类就不应该是这个超类的子类。只有在分类学角度上有意义时,才可以使用继承。不要从工具类继承。错误的使用继承而不是合成/聚合的一个常见原因是错误地把Has-A当成了 Is-A。Is-A代表一个类是另外一个类的一种;Has-A代表一个类是另外一个类的一个角色,而不是另外一个类的特殊种类。例如,我们需要办理一张银行卡,如果银行卡默认都拥有了存款、取款和透支的功能,那么我们办理的卡都将具有这个功能,此时使用了继承关系,如图10-7所示。为了灵活地拥有各种功能,此时可以分别设立储蓄卡和信用卡两种,并有银行卡来对它们进行聚合使用。此时采用了合成复用原则,如图10-8所示。名师资料总结-精品资料欢迎下载-名师精心整理-第 14 页,共 15 页 -名师资料总结-精品资料欢迎下载-名师精心整理-第 15 页,共 15 页 -