作业二:软件设计原则与设计模式
读书笔记----软件设计原则,设计模式
这个作业属于哪个课程 | 2022软件代码开发技术 |
---|---|
这个作业要求在哪里 | 读书笔记----软件设计原则、设计模式 |
这个作业的目标 | 了解软件设计原则和设计模式 |
参考书籍
《Head First 设计模式》:是2007年中国电力出版社出版的图书,作者是(美)弗里曼(Freeman,E.)
内容简介:《Head First设计模式》(中文版)共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式。前言先介绍这本书的用法;第1章到第11章陆续介绍的设计模式为Strategy、Observer、Decorator、Abstract Factory、Factory Method、Singleton,Command、Adapter、Facade、TemplateMethod、Iterator、Composite、State、Proxy。最后三章比较特别。第12章介绍如何将两个以上的设计模式结合起来成为新的设计模式(例如著名的MVC模式),作者称其为复合设计模式(这是作者自创的名称,并非四人组的标准名词),第13章介绍如何进一步学习设计模式,如何发觉新的设计模式等主题,至于第14章则很快地浏览尚未介绍的设计模式,包括Bridge、Builder、Chain of Responsibility、Flyweight、Interpreter、Mediator、Memento、Prototype,Visitor。第1章还介绍了四个○○基本概念(抽象、封装、继承、多态),而第1章到第9章也陆续介绍了九个○○原则(Principle)。千万不要轻视这些○○原则,因为每个设计模式背后都包含了几个○○原则的概念。很多时候,在设计时有两难的情况,这时候我们必须回归到○○原则,以方便判断取舍。可以这么说:○○原则是我们的目标,而设计模式是我们的做法。
全书概况
1.欢迎来到设计模式世界:设计模式入门
2.让你的对象知悉现况:观察者模式
3.装饰对象:装饰者模式
4.烘烤OO的精华:工厂模式
5.独一无二的对象:单件模式
6.封装调用:命令模式
7.随遇而安:适配器与外观模式
8.封装算法:模板方法模式
9.管理良好的集合:迭代器与组合模式
10.事物的状态:状态模式
11.控制对象访问:代理模式
12.模式中的模式:复合模式
13.真实世界中的模式:与设计模式相处
14.剩下的模式
设计模式(穿插设计原则)
一、欢迎来到设计模式世界:设计模式入门
使用设计模式最好的方式是:“把模式装到脑子里,然后在你的设计和应用中,寻找何处可以使用它们。”
当你写好了父类,子类中有些对象需要填加新的改变,而有的不需要,这时你会选择怎么办,在父类中添加一个新的方法吗?那么所有子类都需要继承,有的则需要覆盖,这样一来是不是需要改动的地方很多。增加一个新的改变的接口?但是问题又来了,接口可能无法达到代码的复用。这意味着无论何时你需要修改某个行为,你必须得往下追踪并在每一个定义改行为的类中修改,是不是很麻烦。
这个时候我们可以把新方法独立独立成为一个新类,通常类一般是既有状态又有方法,我们一般认为其是东西,其实也可以是行为,行为也可以有状态和方法。
设计原则:
(1)找出应用中需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
(2)针对接口编程,而不是针对实现编程。
(3)多用组合,少用继承。
策略模式:定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
二、让你的对象知悉现况:观察者模式
出版者+订阅者 = 观察者模式
比如订阅报纸,订阅天气预报,负责发布的我们称为“主题”,订阅者称为“观察者”。
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者收到通知。
观察者模式提供了一种对象设计,让对象和观察者之间松耦合,关于松耦合,松耦合可以两个对象在不清楚彼此细节的情况下依然可以交互。在观察者模式中,被观察者不知道观察者的细节,只知道观察者实现了观察者接口。
设计原则:
(1)为了对象之间的松耦合设计而努力。
观察者模式:定义了对象的一对多依赖,这样一来,当一个对象改变状态的时候,它的所有依赖者都会受到通知并自动更新。
三、装饰对象:装饰者模式(给爱用继承的人一个全新的设计眼界)
当一个对象和另一个对象拥有相同的超类型时,我们可以用一个对象包装那个对象,在任何需要那个被包装的对象的时候,我们都可以使用装饰过的对象代替它。
继承属于扩展形式之一,但不见得达到弹性设计的最佳方式。而组合和委托可于在运行时动态的加上新行为。
设计原则:
(1)类应该对扩展开发,对修改关闭
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰着提供了比继承更有弹性的替代方案。
四、烘烤OO的精华:工厂模式
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
将创建对象的代码集中在一个对象或者方法中,可以避免代码中的重复,并且更方便以后的维护。这也是针对接口编程而不是针对实现编程,这让代码更具有弹性。工厂方法用来处理对象的创建,并将这样的行为封装在子类中,这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。
关于简单工厂和工厂方法之间的差异:简单工厂不是一个真正的设计模式,简单工厂可以将对象的创建封装起来,也就是说全部的事情在一个地方做完,简单工厂不能变更正在创建的产品,然后工厂方法却是创建一个框架,让子类决定如何去实现。
设计原则:
(1)依赖倒置原则——要依赖抽象,不要依赖具体类。
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
抽象工厂模式:提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
五.独一无二的对象:单件模式(用来创建独一无二的,只能有一个实例的对象的入场券)
有一些对象其实我们只需要一个,比如说:线程池(threadpool)、缓存(cache)、对话框、处理偏好设置和注册表(registry)的对象、充当打印机、显卡等设备的驱动程序的对象。这时候就可以考虑单件模式了。
使用单件模式的时候要小心多线程问题。
单件模式为什么是独一无二的呢?
public class Singleton{
private static Singleton uniqueInstance;
private Singleton(){}
public static synchronized Singleton getInstance()
{
if(uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
原因有两个:
(1)构造器是私有的。
(2)创建的时候判断是否存在,如果存在即刻返回对象。
单件模式:确保一个类只有一个实例,并提供一个全局访问点。
单例模式分为饿汉式和懒汉式
饿汉式:类加载就会导致该单实例对象被创建
public final class Singleton implements Serializable {
private Singleton() {}
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance() {
return INSTANCE;
}
public Object readResolve() {
return INSTANCE;
}
}
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
public final class Singleton {
private Singleton() { }
private static Singleton INSTANCE = null;
public static synchronized Singleton getInstance() {
if( INSTANCE != null ){
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
六、封装调用:命令模式(把方法调用封装起来)
“发出请求的对象”和“接受与执行这些请求的对象”分割开来。
命令模式执行流程
(1)动作和接收者在命令对象中被绑在一起。
(2)命令对象提供一个方法execute().这个方法封装了之前绑定的动作,只要调用这个方法就会调用接收者的这些动作。
(3)客户在调用者对象上调用setCommand()方法,并把它传入命令对象。该命令对象被储存在其中,以后需要用到。(一个命令被加载到调用者,该命令可以被使用并丢弃,或者可以被保留下来并使用许多次。)
(4)未来的某个时间点,调用者将调用命令对象的excute()方法。
(5)这导致接收者的动作被调用。
关于撤销命令:
可以用一个堆栈记录操作过程的每一条命令,然后,不管什么时候按下了撤销按钮,你都可以从堆栈中取出最上层的命令,然后调用它的撤销方法。
设计原则:
命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
空对象:当你不想返回一个有意义的对象时,空对象就很有用。客户也可以将处理null的责任转移到空对象。
七、适配器模式与外观模式
客户使用适配器的过程如下:
(1)客户通过目标接口调用适配器的方法对适配器发出请求。
(2)适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。
(3)客户接收到调用的结果,但并未察觉这一切是适配器在起转换作用。
有时候我们需要改写客户端代码来调用新的接口,提供一个适配器类,将所有的改变封装到一个类中,是比较好的做法。根据需要,有时候需要一个适配器包装多个被适配者,有时候需要双向适配器。
对象适配器和类适配器使用了两种不同的适配方法(分别是组合和继承)。
改变接口还有另一个模式,但是它改变接口的原因是为了简化接口,这个模式技术外观模式,之所以这样称呼,是因为它将一个或数个类的复杂的一切都藏到了背后,只显露出一个干净美好的外观。
外观模式并没有“封装”子系统的类,只提供简化的接口
八、封装算法:模板方法模式
认识模板方法,模板方法算作算法的一个模板,在这个模板中,算法的每一个步骤都被方法代表了。某些方法是由这个类(也就是超类)处理的,某些方法则是由子类处理的,需要由子类处理的方法,必须在超类中声明为抽象。
钩子:子类必须实现抽象类的所有抽象方法,在基类中,我们也可以有“默认不做事的方法”或者有默认的实现。我们称这种方法为“hook”(钩子)。子类可以决定要不要覆盖它们。
钩子的作用:
(1)钩子可以让子类实现算法中的可选部分,或者在钩子对于子类的实现并不重要的时候,子类可以不用管该钩子。
(2)钩子也可以让子类有能力为其抽象类作一些决定。
设计原则:
好莱坞原则:别调用我们,我们会调用你。
模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以再不改变算法结构的情况下,重新定义算法中的某些步骤。
比较
依赖倒置原则:教我们尽量避免使用具体类,而多使用抽象,两者的目的都是解耦,但是依赖倒置原则更注重如何在设计中避免依赖。
好莱坞原则:教我们一个技巧,创建一个由弹性的设计,允许底层结能够互相操作,而有防止其他类太过依赖它们。创建框架或组件上的一种技巧,好让低层组件能够挂钩进计算中,而且又不会让高层组件依赖低层组件。
九、管理良好的集合:迭代器与组合模式(管理良好的集合)
它依赖于一个名为迭代器的接口,一旦我们有了这个接口,就可以为各种对象集合实现迭代器:数组、列表、散列表......
迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示。
和单件模式一样,运用迭代器的时候要小心多线程的问题。避免多个迭代器引用同一个对象集合。
内聚:它用来度量一个类或者模块紧密地达到单一目的或责任。
当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关的功能时,我们说它具有低内聚。
我们经常看到一个组件包含另一个组件,而且顶层组件可以负责被包含组件的部分,我们称这种包含其他组件的组件为组合对象,而称没有包含其他组件的组件为叶节点对象。
设计原则:
一个类应该只有一个引起变化的原因。
迭代器模式:提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
组合模式:允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
十、事物的状态:状态模式(策略模式和状态模式是双胞胎,在出生时才会分开)
比较:
状态模式:将行为封装到状态对象中,context的行为随时可以委托到那些状态对象中的一个。当前状态在状态对象集合中改变,以反映出context内部的改变。但是context的客户对此却不甚了解。我们把状态模式看成是不用再context中放置许多条件潘丹的替代方。通过将行为包装进状态对象中,可以通过在context内简单地改变状态对象来改变context的行为。
策略模式:客户通常主动指定context所要组合的策略对象是哪一个。策略模式可以再运行时改变车辆,但对于某个Context对象来说,通常只有一个最适当的策略对象。一般继承一个类的行为时,容易被这个行为困住,难于修改。而策略模式,可以通过组合不同的对象来改变行为。
设计原则:
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
十一、控制对象访问:代理模式(玩过扮白脸,扮黑脸的游戏么?)
远程代理就是好比“远程对象的本地代表”。
虚拟代理作为创建开销大的对象的代表。
你的对象所做的就像是在远程方法调用,但其实只是调用本地堆中的“代理”对象上的方法,再又代理处理所有网络层的底层细节。
使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。
设计原则:
代理模式:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。
比较
代理模式:
①控制对象的访问,代表对象。
②主题不存在的时候可以实例化主题。
③一般不会对一个主题包装多次。
装饰者模式:
①装饰对象,为对象增加行为,改变对象的行为。
②不可以实例化主题。
③可以将一个主题包装多次。
十二、模式中的模式:复合模式(谁料得到居然可以携手合作?)
复合模式:模式通常被一起使用,并被组合在一个设计解决方案中。复合模式在一个解决方案中结合一个或多个模式,以解决一般或重复发生的问题。
MVC是一种范型,它构造代码成为功能段。
用户视图:
设计模式视图:
读书心得
我们并不是为了模式而模式,而是为了解决问题而需要设计模式。用模式并不是为了点缀我们的代码,仿佛不用模式显示不出来我们的代码水平。用模式是因为为了解决这个问题,必须要这么做,不这么做,就违背了OO设计
原则。就会导致维护和理解上的困难。导致项目成本的增加,导致系统质量的降低。
相对而言,GOF的书更像是一个列表,列举了所有的模式,描述了模式是什么,却没有告诉你为什么要这么做。因此,它适合于当你想用一个模式时,帮助你了解模式的细节。
而现实中,往往是先有问题驱动,再有设计模式。当你分析问题,综合考虑各方面的冲击力,最终选择一个模式。或者说这些力量的合力,把你推向一个模式。
最后,这本书还教育我们,设计模式可以组合起来解决问题。一方面我们可以从模式的使用中总结经验,另一方面我们可以根据问题找到新的模式。
确实是一本好书。