一、观察者模式
1、定义:观察者模式定义了对象的一对多依赖,这样一来,当一个对象状态改变状态时,它的所有依赖者都会收到通知并且自动更新。
实现观察者模式的方式很多,但以包含Subject(主题)与Observer(观察者)接口类的设计最常见。
2、类图:
3、松耦合:观察者模式提供了一种对象设计,让主题和观察者之间松耦合。当两个对象松耦合时,它们依然可以交互,但是不太清楚彼此实现的细节。松耦合的设计能够让我们建立有弹性的OO系统,更好的应对变化。因为它把对象之间的依赖降到了最低。
4、JDK中的实现:1)Swing JButton;2)Java.util.Obervable
二、装饰者模式
1、开放——关闭原则(open-closed):类应该对扩展开放,对修改关闭。
2、装饰者模式定义:装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
3、注意:1)装饰者和被装饰者具有相同的超类型;2)可以用一个或多个装饰者包装一个对象;3)装饰者和被装饰者具有相同的超类型,所以在任何需要原始对象(被包装的)场合,可以用装饰过的对象代替它;4)对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者装饰对象;5)装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
4、类图:
5、JDK中的应用:Java I/O。
可以看出,装饰者有一个不太好的地方,它常常会造成设计中有大量的小类,可能会造成使用此API的程序员的困扰。
三、工厂方法模式
(对于简单工厂和工厂方法的区分,有点模糊)
工厂方法模式
1、工厂方法模式定义:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
2、类图:
3、特点:共产方法让子类决定要实例化的类是哪一个,即在编写创建者类时,不需要知道实际创建的产品是哪一个。工厂方法模式将产品的“实现”从“使用”中解耦,对于增加或者改变具体的产品,Creator都不会受到影响。
4、好处:1)将创建对象的代码集中在一个对象或方法中,可以避免代码的重复,并且方便维护;2)客户在实例化对象时,只会依赖于接口,而非具体类,符合依赖倒置原则;3)让代码更具有弹性,便于以后的拓展。
依赖倒置原则(DI):要依赖抽象,不要依赖具体类。
这个原则说明了,不能让高层组件依赖低层组件,而且,不管是高层还是低层组件,两者都应该依赖于抽象。
避免违反DI的方法:1)变量不可以持有具体类的引用;(若使用new,则会持有具体类的引用。可以改用工厂方法来避开)
2)不要让类派生自具体类;
3)不要覆盖基类中已实现的方法。(如果基类中已实现的方法被覆盖,则此基类不是一个真正适合被继承的抽象)。
抽象工厂模式
1、抽象工厂模式定义:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
2、抽象工厂的实现:抽象工厂的方法经常以工厂方法的模式实现。抽象工厂的任务就是定义一个负责创建一类产品的接口,这个接口内的每个方法都负责创建一个具体产品。而这个具体产品的创建,就自然可以利用工厂方法实现。
*3.抽象工厂与工厂方法的比较:(这个可以参考pizza商店的代码,其中pizzastore用的是工厂方法,而pizza的实现用的是抽象工工厂)
1)抽象工厂与工厂方法都是负责创建对象,但抽象工厂是通过对象的组合实现,而工厂方法是通过继承。
工厂方法:利用工厂方法创建对象,需要扩展一个类(例如水果类),并且覆盖它的工厂方法(生产水果的方法)。即通过子类来创建对象(可以有西红柿类、葡萄类等)。
抽象工厂:抽象工厂提供一个用来创建一个产品家族的抽象类型(例如水果工厂),这个类型的子类定义了产品被生产的方法。(子类可以有葡萄工厂、西红柿工厂,这些工厂里面定义了葡萄、西红柿的生产方法)要想使用这个工厂,必须先实例化它,然后将其传入一些针对抽象类型所写的代码中。(例如水果蛋糕生产店,可在里面传入抽象的水果工厂,但实际的水果类型要根据蛋糕的类型来决定)
2)共同的优点:都能将对象的创建封装起来,使应用程序解耦,并降低其对特定实现的依赖。
3)使用的场景:
抽象工厂:需要创建产品家族和让制造的相关产品集合起来。
工厂方法:目前还不知道将来需要哪些实例化具体类时。
四、单例模式
1、单例的含义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
2、类图:
*3、实现:
在单例模式中,需要一个私有构造器,静态方法,静态变量。
1)饿汉模式:能够不用加锁,但是在JVM在加载类时就会马上创建此单例,不论此单例日后是否被使用。
2)懒汉模式
① 只在getInstance()上加synchronized的做法:每次检查此单例是否创建都要加锁,同步累赘。
②使用“双重检查加锁”的做法:只有第一次创建的时候才会加锁。
//防止指令重排序 ,出现返回一个null的实例情况
private volatile static Singleton uniqueSignleton;
private Singleton(){}
public Singleton getInstance(){
//其实可以只要锁内一个检查,此检查是为了提高性能,只在第一次创建时加锁。后续都不需要加锁。
if(uniqueSignleton == null){
synchronized (Singleton.class) {
// 第二次检查,为了防止产生多个Sigleton实例。因为可以有多个线程进入同步代码块外面,
// 当第一个线程创建完,就会退出同步代码块,此时如果不检查,那么第二个进入的纤层也可以创建一个实例。
if(uniqueSignleton == null){
uniqueSignleton = new Singleton();
}
}
}
return uniqueSignleton;
}
注意,单例模式是不能被继承的,因为其构造器是私有化的,而子类不能继承父类私有化的构造器。
五、命令模式
1、定义:命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
2、类图
3、使用场景:当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式。其可以用来实现日志或事务系统。
六、适配器模式与外观模式
适配器模式
1、定义:适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
PS:这里面我们所说的都是对象适配器,适配器还有另一种,类适配器。
2、使用过程
1)客户通过目标接口调用适配器的方法对适配器发出请求;
2)适配器使用被适配者的接口把请求转换成被适配者的一个或多个调用接口;
3)客户受到调用的结果,但并未察觉这一切是适配器在起作用。
可,客户和被适配者是解耦的,客户不清楚适配器的存在。
举例:想用火鸡冒充鸭子,则要有一个火鸡适配器(鸭子适配器持有火鸡对象),火鸡为被适配者。
3、类图
4、优点
1)该模式有良好的OO设计原则:使用对象组合,以修改的接口包装被适配者;
2)被适配者的任何子类,都能够搭配着适配器使用。
5、类适配器
1)使用:类适配器需要类的多重继承,故需要类能多重继承的语言。
2)类图
3)与对象适配器的区别
1.类适配器利用继承的方式,而对象适配器利用组合的方式将请求传送给适配者;
2.类适配器只能够采用某个特定的被适配类,但其不需要实现它的整个类适配者,必要的时候,它还可以覆盖被适配者的行为;
3.对象适配器可以适配适配者及其子类,且需要一个适配器和一个适配者。
6、缺点:适配器无法实现一个没有的功能,故只能抛出异常。客户必须小心潜在的异常。
7、适配器模式和装饰者模式的不同
二者都是用于包装类的接口,但适配器模式是使一个接口变成另一个接口,而装饰者模式则是拓展所包装的类对象的行为或责任,并不改变类的接口。
外观模式(Facade-Pattern)
1、定义:外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高级接口,让子系统更容易使用。
2、类图
3、优点:不仅简化了接口,也将客户从组件的子系统中解耦。
4、与适配器模式的区别:外观模式的意图在于简化接口,而适配器模式的意图在于把一个接口转换为另一个接口。
5、“最少知识原则”——一个新的OO设计原则(也叫demeter法则)
1)定义:只和你的密友谈话。
这个原则告诉我们,要减少对象之间的交互。它希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中一部分,会影响其他部分。否则这个系统会成为一个易碎的系统。
2)应遵循方针
就任何对象而言,在该对象的方法内,我们应该只调用属于以下范围内的方法:该对象本身;被当作方法的参数而传递进来的对象;此方法创建或实例化的任何对象;对象的任何组件(把组件想象成是被实例变量所引用的任何对象)。
3)优点:减少了对象之间的依赖,减少了软件维护成本。
4)缺点:会导致更多的“包装类”被制造出来,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并降低运行时的性能。
5)违反例子:System.out.println()
七、模板方法模式
1、定义:模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
2、类图
原语操作:是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性。即原语的执行必须是连续的,在执行过程中不允许被中断。
抽象类中具体定义
注意:模板方法要定义为final,不可让子类更改;需要让子类重写的方法,一定要定义为抽象方法。
3、钩子方法
1)含义:它是一种被声明在抽象类中的方法,但只有空的和默认的实现。
2)使用场景:模板方法中子类可选的部分可用钩子方法,子类必须实现的部分用抽象方法。
3)钩子的目的:让子类实现算法中可选的部分;让子类能够对模板方法中某些即将发生(或刚刚发生的)步骤作出反应。
4、好莱坞原则——新的OO设计原则
1)定义:别调用我们,我们会调用你。
2)优点:防止“依赖腐败”。在好莱坞原则中,允许底层组件将自己挂钩到系统上,但是由高层组件决定什么时候调用低层组件。
依赖腐败:高层组件和底层组件相互依赖,同时高层组件依赖边侧组件,边侧组件又依赖底层组件。这个时候系统的设计将会变得难以理解。
3)好莱坞原则与依赖倒置:两者都致力于解耦,但依赖倒置更注重在设计中避免依赖;而好莱坞原则则允许低层结构能够互相操作,又防其他类太过依赖它们。
4)模板方法中的好莱坞原则:抽象类相当于高层组件,子类相当于低层组件。如果继承模板方法抽象类的子类没有被调用,则其一定不会直接调用抽象类。
其他实现了好莱坞原则还有工厂方法、观察者模式等等。
5、模板方法的实际应用
1)AVA数组类中的排序模板sort(Object[] o)。它依赖子类重写的compareTo()来排序。(区分策略模式,可见书本P305)
2)inputStream.io.read()方法,它是由子类实现的。而这个方法又会被read(byte b[],int off,int len)模板方法调用。
3 )Swing的JFrame中的paint()是一个钩子方法。
4)Applet类的init()、start()等钩子方法。具体的applet类使用大量钩子来提供行为。
6、模板方法和策略的比较(P309)
我不太懂策略模式,所以直接看书好了。
7、工厂方法与模板方法:工厂方法是模板方法的一种特殊版本。
八、组合模式
1、定义: 组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。(即,忽略对象组合和个别对象的差别)
2、类图
3、单一职责与透明性:组合模式中,一个组件的接口包含组合(包含其他组件的组件)和叶节点(不包含其他组件的的组件),它似乎违反了类的“单一职责”设计原则。但是它能够换取透明性。即,一个元素究竟是叶节点还是组件,对客户是透明的。
九、状态模式
1、定义:状态模式允许对象在内部状态改变时改变它的行为,
...我把后面的复合模式的笔记都做完了,一打开只到这,不用问了,又是提交的时候优快云没检验登陆是否过期直接显示提交成功,结果登陆过期了啥都没记上去。我苦苦地写了半天的笔记没了。呵呵。
(这下面的笔记我之前已经做过了,又丢了,我不知道是因为自己打开了两个窗口保存了旧的一个导致丢了还是怎么样,所以我再看看会不会出现这种情况)
十二、与设计模式相处
1、定义设计模式:模式是指在某情境下,针对某问题的某种解决方案。
情境:即应用某个模式的情况。
问题:即在某情境下想达到的目标,或某情境下的约束。
解决方案:一个通用的设计。
模式必须应用于一个不断重复的问题。
2、设计模式分类
1.按模式目标分类:创建型、行为型、结构型。
创建型:其设计到将对象实例化,这类模式都提供一个方法,将客户从所需要实例化的对象中解耦。
行为型:其涉及到如何和对象交互分配职责。
结构型:其可以让你把类或对象组合到 更大的结构中。
2.按照模式所处理的是类还是对象来分
类模式:描述类之间的关系如何通过继承定义。类模式的关系是在编译时建立的。
对象模式:描述对象之间的关系,而且主要利用组合定义。对象模式的关系通常在运行时建立,更加动态和弹性。
(现在为11月10日)
3、反模式