第四章 开闭原则
开闭原则讲的是:一个软件实体应该对扩展开放,对修改关闭。
与其他设计原则的关系
做到开闭原则不是一件很容易的工作,但是也是有很多规律可循的。这些规律也是同样以设计原则的身份出现,但是他们都是“开闭原则的手段和工具,是附属开闭原则的”。
里氏代换原则:
里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
依赖倒转原则:
依赖倒转原则讲的是,要依赖抽象,不要依赖与现实。
合成聚合复用原则:
合成聚合复用原则讲的是,要尽量使用合成聚合,而不是继承关系达到复用的目的。
迪米特法则:
迪米特法则讲的是,一个软件实体应当与尽可能少的其他实体发生相互作用。
接口隔离原则:
接口隔离原则讲的是,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口。
一个重构做法的讨论
“将条件转移语句改写成多态性”是一条广为流传的代码重构做法。它的意思是说,将一个进行多次条件转移的商业逻辑封装到不同的具体子类中去,从而使用多态性代替条件转移语句。
何时使用:
如果一个条件转移语句确实封装了某种商务逻辑的可变性,那么将此种可变性封装起来就符合开闭原则设计思想了。
但是,如果一个条件转移语句没有涉及重要的商务逻辑,或者不会随着时间的变化而变化,也不意味任何可扩展性,那么它就没有涉及任何有意义的可变性。
第五章 java语言的接口
接口是对可插入性的保证
关联的可插入性,调用的可插入性。
在理想的情况下,一个具体java类应当只实现java接口和抽象java类中声明过的方法,而不应当给出多余的方法。
问题五:Java语言是怎样做到类型安全的?
简单地讲,java语言依靠三种机制做到了这一点:编译期间的类型检查,自动的存储管理,数组的边界检查。
第六章 抽象类
具体类不是用来继承的,只要有可能,不要从具体类继承。
代码重构的建议:
如果在一个原始的设计里,有两个具体类之间有继承关系,那么最可能的修改方案是:
建立一个抽象类或者接口C,然后让类A和类B成为抽象类C的子类。
抽象类应当拥有尽可能多的共同代码
在一个从抽象类到多个具体类的继承关系中,共同的代码应该尽量移动到抽象类里。
一个对象从超类继承而来的代码,在不使用时不会造成对资源的浪费。
抽象类应该拥有尽可能少的数据
与代码移动方向相反的是,数据的移动方向时从抽象类到具体类。一个对象的数据不论是否使用都会占用资源,因此数据应该尽量放到具体类或者等级结构的低端。
基于抽象类的模式和原则
针对抽象编程,不要针对具体编程。这就是依赖倒转原则。
什么时候才能使用继承
1.子类是超类的一个特殊种类,而不是超类的一个角色,is a。
2.永远不会出现需要将子类转成另一个类的子类。
3.子类具有扩展超类的责任,而不是具有置换掉或注销掉超类的责任。
4.只有在分类学角度上有意思时,才可以使用继承,不要从工具类继承。
解释:
3.如果一个子类需要将继承自超类的责任取消或置换后才能使用的话,很有可能这个子类根本就不是那个超类的子类。
如果狗继承猫,猫有上树的能力,狗没有。为了继承关系成立,只好将猫上树的能力取消掉,这个继承关系显然是错误的。
正确的方法是建立一个抽象类,然后继承之。
第七章 里氏代换原则
里氏代换原则
里氏代换原则的严格表达是:
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序p在所有的对象o1都代换成o2时,程序p的行为没有变化,那么类型T2是类型T1的子类。
换言之,一个软件实体如果使用的是一个基类的话,那么一定使用于其子类,而且它根本不能察觉出基类对象和子类对象的区别。
Java语言对里氏代换的支持
在编译期,java 语言编译器会检查一个程序是否符合里氏代换,当然这是一个无关实现的、纯语法意义上的检查。
问题:基类public子类可以private吗?
从里氏代换角度考虑是不可以的,因为客户端完全有可能调用超类型的公开方法。如果以子类代之,这个方法是不可以被调用的,所以就会出现错误。
从代码重构的角度理解
如果A和B违反了里氏代换原则,那么怎么修改呢?
方法一,创建一个新的抽象类C,然后继承之。
方法二,从B到A的继承关系改为委派关系。
第八章 依赖倒转原则
依赖倒转原则讲的是,要依赖抽象,不要依赖具体。
简单地说,传统的过程性系统的设计太过依赖与底层,从而使抽象层次依赖与具体层次,倒转原则是要把这个错误的依赖关系倒转过来,这就是“依赖倒转原则”的由来。
什么是依赖倒转原则
简单地说,依赖倒转原则要求客户端依赖与抽象耦合。依赖倒转原则的表述是:抽象不应当依赖于细节;细节应当依赖于抽象。
依赖倒转原则的另一种表述是:
要针对接口编程,不要针对实现编程。
第二种表述是:
针对接口编程的意思就是说,应当使用java接口和抽象java类进行变量的类型声明、参数的类型声明、方法的返回类型声明,以及数据类型的转化等。
倒转依赖关系强调一个系统内部的实体之间关系的灵活性。基本上,如果设计师希望遵守开闭原则,那么依赖倒转原则便是达到要求的途径。
变量的静态类型和真实类型
List employees = new Vector();
前面的List是静态类型,后面的是真实类型。
引用对象的抽象类型
在很多情况下,一个java程序需要引用一个对象。这个时候,如果这个对象有一个抽象类型的话,应当使用这个抽象类型作为变量的静态类型。这就是针对接口编程的意义。
只要一个被引用的对象存在抽象类型,就应当在任何引用此对象的地方使用抽象类型,包括参量的类型声明,方法返还类型的声明,属性变量的类型声明等。
怎样做到依赖倒转原则
以抽象方式耦合式依赖倒转原则的关键。由于一个抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以改换成其子类,因此,里氏代换原则是依赖倒转原则的基础。
第九章 接口隔离
接口隔离原则讲的是,使用多个专门的接口比使用单一的总接口要好。
角色的合理划分
将“接口”理解为一个类所提供的所有方法的集合,也就是一种在逻辑上才存在的概念。这样的话,接口的划分就直接带来类型的划分。
一个接口相当于剧本中的一个劫色,而此角色在一个舞台上由哪一个演员来演则相当于接口的实现。因此,一个接口应当简单地代表一个角色,而不是多个角色。如果系统涉及到多个角色的话,那么每一个角色都应当由一个特定的接口代表。
为了避免混淆,将这种角色的划分的原则叫做角色隔离原则。
定制服务的例子
定制服务也是一个重要的设计原则。它的意思是说,果如客户端仅仅需要某一些方法的话,那么就应当向客户端提供这些需要的方法,而不要提供不需要的方法。这样的效果是什么呢?
1. 这样做很整洁。从美学的角度上考虑,这是一个很好的做法。从这样的一个设计可以看出,设计师花了很多时间再分析和规划这些接口上面。
但是这并不是最重要的效果,没有人会仅仅因为美学效果而将这一原则当做面向对象的设计原则。
2. 系统的可维护性。想客户端提供piblic接口是一种承诺,一个piblic接口一旦提供,就很难撤回。作为软件提供商,没有人愿意做出过多的承诺,特别是不必要的承诺。过多的承诺会给系统的维护造成不必要的负担。
第十章 合成/聚合复用原则
这个设计原则的一个简短表述:要尽量使用合成/聚合,尽量不要使用继承。
合成/聚合的区别
聚合用来表示“拥有”关系或者整体与部分的关系;而合成则用来表示一种强得多的“拥有关系”。在一个合成关系里,部分和整体的生命周期是一样的。一个合成关系中的成分对象是不能和另一个合成关系共享的。一个成分对象在同一个时间内只能属于一个合成关系。
继承复用的优点
新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入到子类。
修改或扩展继承而来的实现较为容易。
继承复用的缺点
继承复用破坏包装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常对子类透明的,因此这种复用是透明的复用,又称白箱复用。
如果超类的实现发生变化,那么子类的实现也不得不发生变化。
从超类继承而来的实现是静态的,不可能在运行时间内发生改变,因此没有足够的灵活性。
区分“Has a”与“Is a”
“Is a”是严格的分类学意义上的定义,意思是一个类是另一个类的“一种”。而“Has a”则不同,它表示某一个角色具有某一项责任。
导致错误地使用继承而不是合成/聚合的一个常见的原因是错误地把“Has a”当做“Is a”。“Is a”代表一个类是另一个类的一种;“Has a”代表一个类是另一个类的一个角色,而不是另一个类的一个特殊种类。这是coad条件的第一条。
请参考下面一个例子:“人”被继承到“雇员”“经理”“学者”等子类。而实际上,“雇员”“经理”“学者”分别描述一种角色,而“人”可以同时有几种不同的角色。比如,一个“人”既然是“经理”,就必定是“雇员”;而此人可能同时参加了MBA课程,从而也是一个学生。使用继承来实现角色,则只能使每个人具有HAS A角色,而且继承是静态的,这会使得一个人在成为雇员身份后,就永远为雇员,不能称为经理或者学生,而这显然是不合理的。
改法:使“雇员”“经理”“学者”继承自角色这个类,同时“人”和角色是组合关系。
与里氏代换原则联合使用
里氏代换原则是继承复用的基石,如果在任何使用B类型的地方都可以使用S类型,那么S类型才能称为B类型的子类型,而B类型才能称为S类型的基类型。
换言之,只有当每一个S在任何情况下都是一种B的时候,才可以将S设计成为B的子类。如果两个类的关系是“HAS A”关系而不是“Is a”关系,这两个类一定违反里氏代换原则。
只有两个类满足里氏代换原则,才有可能是“Is a”关系。
第十一章 迪米特法则LoD
迪米特法则又叫做最少知识原则,就是说,一个对象应当对其他对象有尽可能少的了解。
迪米特法则的众多描述:
只与你直接的朋友们通信。
不要跟陌生人说话
每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
狭义的迪米特法则
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
朋友圈的确定
当前对象本身
以参量形式传入到当前对象方法中的对象
当前对象的实例变量直接引用的对象
当前对象的实例变量时一个聚集,那么聚集中的元素也都是朋友
当前对象所创建的对象
狭义的迪米特法则的缺点
遵循狭义的迪米特法则会产生一个明显的缺点:会在系统里造出大量的小方法,散落在系统的各个角落。这些方法仅仅是传递间接调用,因此与系统的商务逻辑无关。当设计师试图从一张类图看出总体的架构时,这些小的方法会造成迷惑和困扰。
遵循类之间的迪米特法则会使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关系。但是,这也会造成系统的不同模块之间的通信效率降低,也会使系统的不同模块之间不容易协调。
与依赖倒转原则互补使用
为了克服狭义的迪米特法则的缺点,可以使用依赖倒转原则,引入一个抽象类型引用“抽象陌生人”变成朋友,入下图所示:
广义的迪米特法则
其实,迪米特法则所谈论的,就是对对象之间的信息流量、流向以及信息的影响的控制。
在软件系统中,一个模块设计得好不好的最主要、最重要的标志,就是该模块在多大的程度上将自己的内部数据和其他与实现有关的细节隐藏起来。一个设计得好的模块可以将它多有的实现细节隐藏起来,彻底地将提供给外界的API和自己的实现分割开来。
迪米特法则的主要用意是控制信息的过载。在将迪米特法则运用到系统设计中时,要注意下面几点:
在类的划分上,应当创建有弱耦合的类。类之间的耦合越弱,就越有利于复用。一个处在弱耦合中的类一旦被修改,不会对有关系的类造成波及、
在类的结构设计上,每一个类都应当尽量降低成员的访问权限。换而言之,一个类包装好各自private状态。这样一来,想要了解其中的一个类的意义时,就不需要了解很多别的类的细节。一个类不应当public自己的属性,而应当提供取值和赋值方法让外界间接访问自己的属性。
在类的设计上,只要有可能,一个类应当设计成不变类。
在对其他类的引用上,一个对象对其对象的引用应当降到最低。
谨慎使用Serializable,这个没太懂哇。
广义迪米特法则在层次上的实现
限制局域变量的有效范围
变量不用全部放在总代码的头部,这样有两个好处:
程序员可以很容易读懂程序。
如果一个变量是在需要它的程序块外部声明的,那么当这个快还没有被执行时,这个变量就已经被分配了内存,而在这个程序块已经执行完毕后,这个变量所占据的内存空间还没有被释放,这显然是不好的。
所谓迪米特方法,就是设法使一个软件系统的不同对象彼此之间尽量“老死不相往来”,降低系统维护成本的法则,它与老子的“小国寡民”的统治之术不谋而合。
发表评论
-
设计模式记录(3.2)
2010-09-08 21:36 758第二十五章 合成模式 合成模型模式属于对象的结构模式, ... -
设计模式记录(3.1)
2010-09-08 13:16 715第四部分 结构模式 第二十二章 适配器模式 适配器模式 ... -
设计模式记录(2.4)
2010-09-06 13:35 784第十二章 原始模型模式 原始模型模式属于对象的创建模式 ... -
设计模式记录(2.3)
2010-09-03 23:20 767第十九章 建造模式 建造模式似乎对象的创建模式。建造模 ... -
设计模式记录(2.2)
2010-09-03 08:30 858第十五章 单例模式 单 ... -
设计模式记录(2.1)
2010-09-01 20:04 884第十二章 简单工厂模式 ...
相关推荐
这个压缩包文件"设计模式(包含5个设计模式)含源代码报告.rar"显然是一份宝贵的资源,它涵盖了五个核心的设计模式,并附带了详细的类图、源代码以及文档报告,这对于学习和理解设计模式至关重要。 首先,我们要探讨...
《新版设计模式手册 - C#设计模式(第二版)》是一部深入探讨C#编程中设计模式的权威指南,尤其适合已经有一定C#基础并希望提升软件设计能力的开发者阅读。设计模式是解决软件开发中常见问题的经验总结,是软件工程的...
设计模式是软件工程中的一种重要概念,它代表了在特定情境下解决常见问题的最佳实践。刘伟先生在讲解设计模式时,通常会深入浅出地介绍这些模式的原理、应用场景以及如何有效地在实际编程中应用它们。设计模式并不是...
设计模式的学习过程通常分为四个阶段:学习、表达、教授、记录。每个阶段都需要不同的技能和深度的理解。 #### 0.2 设计模式解析后记 在完成所有设计模式的学习和解析之后,开发者会发现自己已经进入了一个新的...
《Java设计模式之禅》是一本深入浅出讲解设计模式的书籍,书中不仅包含23种经典设计模式的案例,还详细介绍了设计模式背后的思想和原则,适合初学者以及对设计模式有一定了解的程序员阅读。本书旨在帮助读者理解如何...
在本项目“设计模式期末大作业 ToFu”中,开发者运用了多种设计模式来构建一个豆腐商店的应用。这个应用展示了如何在实际编程中灵活运用设计模式来提高代码的可读性、可维护性和可扩展性。以下是每个设计模式的详细...
备忘录模式记录一个对象的内部状态,以便以后恢复。状态模式允许对象在其内部状态改变时改变其行为。访问者模式定义了一个访问者接口,该接口用于访问一个对象结构中的元素。解释器模式提供了语言的表达式结构和一个...
设计模式是一种在软件设计中被广泛认可的解决特定问题、经过良好记录并可重用的解决方案。它们代表了在各种编程环境中反复出现的问题的最佳实践。原版的设计模式通常指的是最初由“Gang of Four”(GoF)在他们的...
1. **种类**:共有23种设计模式被记录在著名的《设计模式:可复用面向对象软件的基础》一书中,这些模式由Erich Gamma等四位作者提出,因此也被称为GOF设计模式。 2. **分类**:这23种模式可以根据其目的分为三大类...
设计模式是软件工程中的一种最佳实践,用于解决在软件开发过程中常见的问题,提供了一套可重用的解决方案。在Java编程语言中,设计模式的应用尤为广泛,因为它们可以帮助开发者构建可扩展、可维护且易于理解的代码。...
1. **创建型设计模式**:这类模式主要关注对象的创建过程,使代码能够灵活地应对不同的对象构造方式。包括: - 单例模式(Singleton):保证一个类只有一个实例,并提供全局访问点。 - 工厂方法模式(Factory ...
设计模式是软件工程中的一种最佳实践,用于解决在开发过程中常见的设计问题,提高代码的可重用性、可维护性和可扩展性。本作业涵盖了多种设计模式,包括结构型、行为型和创建型模式,旨在帮助学习者深入理解和应用...
《Gof设计模式设计模式设计模式PDF》是一个深入学习设计模式的重要资源,包含了全面而详尽的设计模式理论和实践知识。设计模式是软件工程中的一种最佳实践,它为解决常见问题提供了一种可复用的解决方案,使得代码...
设计模式是软件开发中一种广泛采用的实践,它代表了在特定上下文中解决常见问题的通用解决方案。设计模式并非具体的代码或库,而是对最佳实践的描述,它们是经过时间验证、可重用的代码设计模式,旨在提高代码的...
根据提供的文件信息,“设计模式书记pdf”是一本详细介绍多种设计模式的书籍,旨在通过形象有趣、生动活泼的方式帮助读者理解和掌握这些模式。接下来,我们将基于这个背景信息来展开相关的知识点。 ### 设计模式...
在本设计模式课程设计中,我们重点探讨了五个核心的设计模式:原型模式、单例模式、抽象工厂模式、代理模式和建造者模式。这些模式在Java编程中具有广泛的应用,能够帮助开发者创建更加灵活、可扩展和易于维护的代码...
1. 工厂模式:工厂模式是一种创建型设计模式,提供了一种创建对象的最佳方式,通过抽象工厂接口,避免了代码与具体类的耦合,提高了系统的可扩展性。 2. 单例模式:单例模式确保一个类只有一个实例,并提供全局访问...
### Java设计模式详解 在软件开发领域,设计模式是一种被广泛采用的解决方案,用来解决常见的设计问题。设计模式不仅能够帮助开发者写出可复用、可维护的代码,还能提高团队间的沟通效率。以下是对给定文件中提到的...
从给定的文件信息来看,该文档主要涵盖了GoF(Gang of Four)提出的23种设计模式的详细解析,并附带了C++语言的实现源码。设计模式是在软件工程领域内,针对常见问题的一套经过验证的解决方案,它们为解决特定类型的...