Java中常见的设计模式
Java 中的 23 种设计模式:
Factory(工厂模式)、Builder(建造模式)、Factory Method (工厂方法模式)、
Prototype(原始模型模式)、Singleton(单例模式)、Facade(门面模式)、
Adapter(适配器模式)、Bridge(桥梁模式)、 Composite(合成 模式)、
Decorator(装饰模式)、Flyweight(享元模式)、Proxy(代理模式)、
Command(命令模式)、 Interpreter(解释器模式)、Visitor(访问者模式)、
Iterator(迭代子模式)、 Mediator(调停者模式)、Memento(备忘录模式)、
Observer(观察者模式)、State(状态模式)、Strategy(策略模式)、
Template Method(模板方法模式)、Chain Of Responsibleity(责任链模式)
IO 流使用的是装饰者模式、按钮监听观察者模式、forEach 遍历 List TreeSet 使用策略模式
装饰模式
装饰模式(Decorator)也叫包装器模式(Wrapper)。GOF 在《设计模式》一书中给出的定义为:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。让我们来理解一下这句话。我们来设计“门”这个类。假设你根据需求为“门”类作了如下定义:
现在,在系统的一个地方需要一个能够报警的Door,你来怎么做呢?你或许写一个Door 的子类 AlarmDoor,在里面添加一个子类独有的方法 alarm()。嗯,那在使用警报门的地方 你必须让客户知道使用的是警报门,不然无法使用这个独有的方法。而且,这个还违反了 Liskov 替换原则。 也许你要说,那就把这个方法添加到 Door 里面,这样不就统一了?但是这样所有的门都必须有警报,至少是个“哑巴”警报。而当你的系统仅仅在一两个地方使用了警报门,这明 显是不合理的——虽然可以使用缺省适配器来弥补一下。 这时候,你可以考虑采用装饰模式来给门动态的添加些额外的功能。
装饰着模式的组成:
- 抽象构件角色(Component):定义一个抽象接口,以规范准备接收附加责任的对象
2) 具体构件角色(Concrete Component):这是被装饰者,定义一个将要被装饰增加功能的类。
3) 装饰角色(Decorator):持有一个构件对象的实例,并定义了抽象构件定义的接口。
4) 具体装饰角色(Concrete Decorator):负责给构件添加增加的功能。
类图如下
图中 ConcreteComponent 可能继承自其它的体系,而为了实现装饰模式,他还要实现 Component 接口。整个装饰模式的结构是按照组合模式来实现的——两者都有类似的结构 图,都基于递归组合来组织可变数目的对象。但是两者的目的是截然不同的,组合 (Composite)模式侧重通过递归组合构造类,使不同的对象、多重的对象可以“一视同仁”; 而装饰(Decorator)模式仅仅是借递归组合来达到定义中的目的
// 简单案例分析
class Person
{
public void eat()
{
System.out.println("吃饭...");
}
}
class newPerson extends Person
{
public void eat()
{
System.out.println("饭前开胃酒...");
super.eat();
System.out.println("饭后甜点...");
System.out.println("饭后休闲...");
}
}
class newWrapperPerson
{
/**
* 装饰者模式:
* 1.定义一个私有的装饰对象
* 2.提供一个带参数的构造函数初始化该对象
* 3.根据需求在原有功能的基础上扩展相应的功能
*/
private Person p;
public newWrapperPerson(Person p)
{
this.p = p;
}
public void eat()
{
System.out.println("开始吃饭、聊天...喝喝小酒...");
p.eat();
System.out.println("饭后甜点...");
System.out.println("饭后休闲...");
System.out.println("吃饭结束...");
}
}
public class WrapperDemo {
/**
* 装饰者模式是为了解决给类的功能增强而出现的
* --对Writer根据不同的业务进行划分
* Writer
* |------TextWriter
* |------MediaWriter
* |------ImageWriter
*
* ---随着业务功能的提升 这时需要对Writer再次改写 要求提升性能 --利用缓冲
* Writer
* |------TextWriter
* |----BufferedTextWriter
* |------MediaWriter
* |---BufferedMediaWriter
* |------ImageWriter
* |---BufferedImageWriter
*
* ---业务进一步提升需求 这时要求 对Writer 提高容错性和完整性 --- 利用差异传输...
* Writer
* |------TextWriter
* |----BufferedTextWriter
* |----InfoExceptionBufferedTextWrite
* |------MediaWriter
* |---BufferedMediaWriter
* |---InfoExceptionBufferedMediaWriter
* |------ImageWriter
* |---BufferedImageWriter
* |----InfoExceptionBufferedImageWriter
*
* 可以利用装饰者模式 解决继承来实现功能增强的这种行为
* Writer
* |---TextWriter
* |---MediaWriter
* |---ImageWriter
* |---BufferedWriter
* |--InfoExceptionWriter
*
* class BufferedWriter extends Writer{
* private TextWriter textWriter;
* public BufferedWriter( TextWriter textWriter){
* this.textWriter=textWriter;
* }
* }
*
* 使用装饰者模式的 特点:
* 1.装饰者中的构造函数 必须接受被装饰的类
* 2.装饰的类和被装饰的类应该属于同一个体系
*/
public static void main(String[] args) {
newWrapperPerson nwp = new newWrapperPerson(new Person());
nwp.eat();
}
}
GOF 书中给出了以下使用情况:
1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2) 处理那些可以撤消的职责。
3) 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类 定义被隐藏,或类定义不能用于生成子类。
透明和半透明:
对于面向接口编程,应该尽量使客户程序不知道具体的类型,而应该对一个接口操作。 这样就要求装饰角色和具体装饰角色要满足 Liskov 替换原则。像下面这样:
Component c = new ConcreteComponent(); Component c1 = new ConcreteDecorator(c);
JUnit 中就属于这种应用,这种方式被称为透明式。而在实际应用中,比如 http://java.io 中往往因为要对原有接口做太多的扩展而需要公开新的方法(这也是为了重用)。所以往往不能对客户程序隐瞒具体的类型。这种方式称为“半透明式”。 在 http://java.io 中,并不是纯装饰模式的范例,它是装饰模式、适配器模式的混合使用。
采用 Decorator 模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅 在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。