这部分太多了,自己学习的时候有侧重,自己整理总结的笔记是纸质版的,学习这部分的时候主要是看了网上的demo,以及《Head First设计模式》(这本书我觉得推荐度一般,因为废话实在太多了...).《大话设计模式》听说很多人买,之前买过《大话数据结构》感觉大话系列的蛮容易看懂的,所以推荐下可以去看这本。
面向对象设计模式可分为三类:(创建、结构、行为)
创建型模式:工厂模式、抽象工厂模式、生成器模式、单例模式、原型模式
结构型模式:(类)适配器模式、(对象)适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式
行为模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
一、创建型模式:(5)
1、工厂模式【工厂模式 - 】
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
2、抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品。
总结:
a.简单工厂,不是设计模式,是一个方法,让客户程序从具体类解耦
b.工厂:使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象;
抽象工厂:对象组合,对象的创建被实现在工厂接口所暴露的方法中。创建相关的对象家族。
c.所有工厂模式都通过减少应用程序和具体类间的依赖促进松耦合。
3、生成器模式:
4、单例模式:可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
私有的实例,私有的构造方法,提供了一个公共的提供唯一实例的方法
使用:用来管理共享的资源,如数据库连接或线程池。
设计思路
要创建的对象的类只有一个私有的构造函数,构造函数中定义了唯一的单例对象,外界只能通过getInstance()方法得到对象实例,且每次都只能生成唯一的对象。
如果不用单例模式而使用全局变量的话:
全局变量可以提供全局访问,但不能确保只有一个实例。用许多全局变量指向许多小对象造成命名空间污染。
单例模式几种形式:Java设计模式之单例模式详解 - garryfu - 博客园
a.饿汉式:立即加载,类加载初期就创建好了静态对象,线程安全的。
```
public class Singleton1{
private Singleton1(){}
private static Singleton1 single=new Singleton1();
public static Singleton1 getInstance(){
return single;
}
}
```
b.懒汉式:延迟加载,类似上图,一开始对象是null,非线程安全。
但会产生多个,所以要加synchronized,加同步锁/同步代码块,但运行效率低下
==>加双重检查和synchronized,优化,避免了整个方法被锁,提高了执行效率
```
双重检查:
public class Singleton4{
private Singleton4(){}
private static Singleton4 single=null;
public statci Singleton4 getInstance(){
if(single==null) {
synchronized(Singleton4.class){
if(single==null){
single=new Singleton4(); }
}
}return single;
}
}
```
c.静态内部类实现
d.静态代码块实现
e.内部枚举类实现
5、原型模式 设计模式之原型模式 -
创建一个对象之后的多个对象都可以拷贝原型来创建。
具体实现是:继承Cloneable接口,重写Clone()方法
二、结构型模式:(7种)
1、适配器模式:
(1)类适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
思路:类适配器,指的是适配器Adapter继承我们的被适配者Adaptee,并实现目标接口Target。
(2)对象适配器模式:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
解决设计思路:
对象适配器,简单的说就是适配器实现我们的目标接口,但是并不继承需要被适配的类。而是通过在适配器的构造函数中将需要被适配的类传递进来从而进行适配。
2、装饰器模式:(最体现扩展性)
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:
可以用一个或多个装饰者包装一个对象。
装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装)的场合,可用装饰过的对象代替它。
装饰者可以在所委托被装饰者的行为之前/之后,加上自己的行为,以达到特定目的。
调用时:被装饰者在最底层,被装饰者一层层包装,可以包装多次。
```
Beverage b=new Beverage();
b=new Mocha(b);
b=new Mocha(b);
b=new Milk(b);
System.out.println(b.getDes()+ b.cost());
```
扩展:java.io包使用了装饰者模式
【抽象组件:InputSream
具体的抽象组件/被装饰类:FileInputStream、StringBufferInputStream、ByteArrayInputStream...
抽象装饰者:FilterInputStream
具体组件/装饰类:PushbackInputStream、BufferedInputStream、DataInputStream
】
缺点是:设计时会加入大量小类。在实例化组件时,还要把此组件包装进去,增加了代码复杂度。
优点:适合建立有弹性的设计、维持开放-关闭原则。
3、代理模式(Proxy):
代理模式就是多一个代理类出来,替原对象进行一些操作。
4、外观模式:
外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例)
5、桥接模式:
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化。
像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:
6、组合模式:
(1)安全组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
(2)一致性组合模式:
7、享元模式:
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
三、行为型模式:(11)
1、策略模式
2、模板方法模式
3、观察者模式:(出版者/主题+订阅者/观察者)
a. 定义了对象之间的一对多依赖,一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
在subject接口和observer接口中直接写就可以了。
在concreteSubject类,要实现subject接口,可以创建一个ArrayList
在concreteObserver类,实现Observer接口,有一个Subject的对象,其构造函数,相当于注册了一个新的观察者,所以是需要subject需要注册的主题为传入参数,并调用其的register()方法,相当于在该主题注册了新对象。其update()方法,当其被concreteSubject的实体对象调用时,会传入参数,也就是给这个观察者对象做了通知。
b. 此外!!!!java API有内置的观察者模式,java.util包有包含基本的Observer接口和Observable类,类似于Subject接口和Observer接口,可以进行push()、pull()传送数据。
Observable接口的方法:addObserver() deleteObserver() notifyObserver() setChanged()
现在concreteSubject类继承Observable类,并继承其addr、remove、notify()等方法。
对concreteObserver,要把对象变成观察者:实现Observer接口,然后构造方法中传入observer对象,并调用其addObserver()方法;不想当观察者,则调用deleteObserver()
观察者送出通知:在concretObserver类观察者中构造concreteSubject类,调用其setChanged()方法,标记状态已经改变;调用其notifyObservers()或notifyObservers(Object arg)//传任何数据对象
【推和拉:
拉:
setChanged():改变状态,到达某种条件,才调用该方法,使changed变为true,进行通知。这样可以避免不必要的通知。这个时候传送通知的方式是“拉”
notyfyObservers(Object arg):源代码会先判断是否发生了changed,true才通知观察者,通知后将changed标志又设回false
推:
没有setChanged(),和之前一样,就是直接notify(),不用通知者自己来调用setChanged()和notify()的,就是推。
】
观察者接收通知:同之前的update(Observerable o,Object arg) //传任何数据对象
但是!!
1、java.util.Observable是一个类,因此某类要同时有Observable和其他超类行为,就会困难(因为单继承),限制了其复用潜力。
2、除非继承Observable类,否则无法创建Observable的实例并组合到你自己的对象中(特别是setChanged()是protected的)
c. JDK中的使用:
JButton
4、迭代子模式、
5、责任链模式
6、命令模式:将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。
命令对象封装动作和接收者,而只暴露出execute(),当该方法调用,接收者会进行动作。
Client:是测试类
Command接口:只有execute()方法
ConcreteCommand类:具体命令,其中有Receiver具体接受类对象,实现了Command接口,其方法主要是重写了execute(),让Receiver对象实现一个具体的命令。
Receiver:也就是命令的执行对象,ConcreteCommand将会调用它
Invoke类:调用者,其有Command对象,在setCommand(command)方法中传入一个具体的concreteCommand对象,设置具体的命令;其还有另外一个方法,用于调用concreteCommand对象的execute()方法
例子:遥控器开灯
应用:队列请求、日志请求
7、备忘录模式
8、状态模式
9、访问者模式
10、中介者模式
11、解释器模式