设计模式
从模式名称(pattern name) ,问题(problem) ,解决方案(solution) ,效果(consequences) 四个方面考虑一个设计模式
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式
还有建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、桥接模式
外观模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、观察者模式、责任链模式.
还有模板方法模式、迭代子模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
设计模式的类型:
- 创建型:对象创建
- 结构型:处理类或对象组合
- 行为型:对类或对象怎样交互和怎样分配职责进行描述
简单工厂模式
也叫静态工厂方法(Static Factory Method)模式,通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类
class Apple implements Fruit {
/** * 采集苹果 */
public void get() {
System.out.println("采集苹果!");
}
}
class Banana implements Fruit {
/** * 采集香蕉 */
public void get() {
System.out.println("采集香蕉!");
}
}
interface Fruit {
public void get();
}
class FruitFactory {
/** * 获得Apple类的实例 */
public static Fruit getApple() {
return new Apple();
}
/** * 获得Banana类的实例 */
public static Fruit getBanana() {
return new Banana();
}
/** * 上述两个方法可以替换成如下的一个方法,减少代码冗余 */
public static Fruit getFruit(String type) throws InstantiationException, IllegalAccessException {
if (type.equalsIgnoreCase("apple")) {
return Apple.class.newInstance();
} else if (type.equalsIgnoreCase("banana")) {
return Banana.class.newInstance();
} else {
System.out.println("找不到相应的实例化类!");
return null;
}
}
}
//方法部分:不使用工厂模式
Fruit apple = new Apple(); // 实例化Apple,这里用到了多态
apple.get(); //实例化
Banana Fruit banana = new Banana();
banana.get();
//使用工厂模式
Fruit apple = FruitFactory.getApple();
Fruit banana = FruitFactory.getBanana();
apple.get();
banana.get();
优点:
- 工厂类是该模式的核心,这个类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,因此客户端可以免除直接创建产品对象的责任,而仅仅负责“消费”产品。
- 通过这种做法实现了对责任的分割。
- 在某种程度上支持“开-闭”原则。
缺点:
- 当产品类有不同的接口种类时,工厂类需要判断在什么时候创建某种产品。
- 这种对时机的判断和对哪一种具体产品的判断逻辑混合在一起,使得系统在将来进行功能扩展时较为困难。
- 只在有限的程度上支持“开–闭”原则。因为接纳新的产品意味着修改这个工厂角色的源代码
工厂方法模式
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
通过创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
工厂方法模式完全支持开闭原则
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
//工厂类接口
public interface Provider {
public Sender produce();
}
//两个工厂类
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
//方法
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
简单工厂和工厂的比较
工厂方法模式的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。
工厂方法模式可以允许很多具体工厂类从抽象工厂类中将创建行为继承下来,从而可以成为多个简单工厂模式的综合,进而推广了简单工厂模式。
工厂方法模式退化后可以变得很像简单工厂模式。
抽象工厂(Abstract Factory)模式
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意。
//抽象工厂
public interface Creator {
public ProductA factoryA();
public ProductB factoryB();
}
//工厂实现1
public class ConcreteCreator1 implements Creator {
public ProductA factoryA() {
return new ProductA1();
}
public ProductB factoryB() {
return new ProductB1();
}
}
//工厂实现2
public class ConcreteCreator2 implements Creator {
public ProductA factoryA() {
return new ProductA2();
}
public ProductB factoryB() {
return new ProductB2();
}
}
//抽象产品1
public interface ProductA {
}
//抽象产品2
public interface ProductB {
}
//产品族1
public class ProductA1 implements ProductA {
public ProductA1() {
}
}
public class ProductB1 implements ProductB {
public ProductB1() {
}
}
//产品族2
public class ProductB2 implements ProductB {
public ProductB2() {
}
}
public class ProductA2 implements ProductA
{
public ProductA2() {
}
}
在真实的系统中,产品等级结构的数目与每个产品等级结构中产品的数目(产品族)一般是不相等的。
产品族:
是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如在下图中,箭头所指就是三个功能相关联的产品,它们位于三个不同的等级结构中的相同位置上,组成一个产品族。
单例模式
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
饿汉式单例
public class EagerSingleton {
private static final EagerSingleton m_instance = new EagerSingleton();
/* 私有的默认构造子 */
private EagerSingleton() {
}
/* 静态工厂方法 */
public static EagerSingleton getInstance() {
return m_instance;
}
}
由于构造方法是私有的,因此,此类不能被继承。
懒汉式单例
类的构造方法同样是私有的。与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化。
public class LazySingleton {
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (m_instance == null) {
file: // More than one threads might be here!!!
synchronized (LazySingleton.class) {
if (m_instance == null) {
m_instance = new LazySingleton();
}
}
}
return m_instance;
}
private static LazySingleton m_instance = null;
}
关于懒汉式和饿汉式的比对,参考:Java单例模式–饿汉式、懒汉式需要怎么写
饿汉式单例类在自己被加载时就将自己实例化。即便加载器是静态的,在饿汉式单例类被加载时仍会将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。
适配器模式
- 将一个类的接口转换成客户希望的另外一个接口。
- Adapter模式使得原本由于接口不兼容而不能一起的那些类可以一起工作。
- Adapter模式也叫做包装器Wrapper。
用途:
- 如果想使用一个已经存在的类,而它的接口不符合要求。
- 如果想创建一个可以复用的类,该类可以与其它不相关的类或不可预见的类协同工作。
- 对于对象适配器,如果想使用一些已经存在的子类,但是不能对每个都进行子类化以匹配他们的接口。
类的适配器模式
核心思想就是:有一个Source
类,拥有一个方法待适配,目标接口是Targetable
,通过Adapter
类,将Source
的功能扩展到Targetable
里
public class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {
/* 与原类中的方法相同 */
public void method1();
/* 新类的方法 */
public void method2();
}
//适配器
public class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
}
//测试方法
Targetable target = new Adapter();
target.method1();
target.method2();
对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。
//其他部分不做修改
public class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {
/* 与原类中的方法相同 */
public void method1();
/* 新类的方法 */
public void method2();
}
//Adapter换为Wrapper
public class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source){
super();
this.source = source;
}
@Override
public void method2() {
System.out.println("this is the targetable method!");
}
@Override
public void method1() {
source.method1();
}
}
//测试方法
Source source = new Source();
Targetable target = new Wrapper(source);
target.method1();
target.method2();
两种适配结果相同,只是适配的方式不同。
另外还有接口的适配器模式,不做讲解。
代理模式
为其他对象提供一种代理以控制对这个对象的访问。
代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。——摘自23种设计模式全解析
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
- 修改原有的方法来适应。这样违反了开闭原则。
- 采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
public interface Sourceable {
public void method();
}
//Source类可以理解为Client
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
//代理类
public class Proxy implements Sourceable {
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
//after和before可有可没有
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
//测试方法
Sourceable source = new Proxy();
source.method();
代理模式的用途(了解)
远程代理( Remote Proxy ): 为一个对象在不同的地址空间提供局部代表。NEXTSTEP[Add94] 使用
NXProxy
类实现了这一目的。Coplien[Cop92] 称这种代理为大使(Ambassador)。远程代理可以隐藏一个对象存在于不同地址空间的事实。
虚代理(Virtual Proxy):根据需要创建开销很大的对象。在动机一节描述的
ImageProxy
就是这样一种代理的例子。(杀鸡儆猴,割发代首)可以进行最优化,例如根据要求创建对象。
保护代理(Protection Proxy):控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。例如,在
Choices
操作系统[ C I R M 9 3 ]中KemelProxies
为操作系统对象提供了访问保护。智能指引(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:
- 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为智能指针)。
- 当第一次引用一个持久对象时,将它装入内存。
- 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
Protection Proxy和Smart Reference都允许在访问一个对象时有一些附加的内务处理(Housekeeping task)。
装饰器模式
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
- 动态地给一个对象添加一些额外的职责,别名也叫Wrapper
- 装饰器必须和要包装的的对象具有相同的接口
- 有时我们希望给某个对象而不是整个类添加一些功能。
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
//装饰器类
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
//测试方法
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
适用性:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤消的职责。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
优点:
Decorator的目的是“动态的给一个对象添加一些额外的功能“ ,其关键之处在于”动态”和“对象”上
传统上依靠子类继承来实现功能的扩展
它针对的是整个类,但Decorator不使用继承,所以它只是针对单个对象实例,进行功能的扩展,并且可以在运行时动态地灵活地对功能进行组装
桥接(Bridge)模式
当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
public interface Sourceable {
public void method();
}
public class SourceSub1 implements Sourceable {
@Override
public void method() {
System.out.println("this is the first sub!");
}
}
public class SourceSub2 implements Sourceable {
@Override
public void method() {
System.out.println("this is the second sub!");
}
}
public abstract class Bridge {
private Sourceable source;
public void method(){
source.method();
}
public Sourceable getSource() {
return source;
}
public void setSource(Sourceable source) {
this.source = source;
}
}
public class MyBridge extends Bridge {
public void method(){
getSource().method();
}
}
//测试方法
Bridge bridge = new MyBridge();
/*调用第一个对象*/
Sourceable source1 = new SourceSub1();
bridge.setSource(source1);
bridge.method();
/*调用第二个对象*/
Sourceable source2 = new SourceSub2();
bridge.setSource(source2);
bridge.method();
策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数
适用于:
- 以不同的格式保存文件
- 以不同的算法压缩文件
- 以不同的算法截获图象
- 以不同的格式输出同样数据的图形,比如曲线 或框图bar
- ….
策略模式的元素组成:
- 策略接口及各类策略的实现
- 环境类,用于传入具体的策略类作为参数
使用时,是通过创建环境类,然后将具体的策略作为环境类的参数使用的。
public interface ICalculator {
public int calculate(String exp);
}
public abstract class AbstractCalculator {
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
//实现类
public class Plus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\+");
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}
public class Multiply extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\*");
return arrayInt[0]*arrayInt[1];
}
}
//测试方法
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
这里有一篇很好的关于策略模式的文章:设计模式学习之策略模式
观察者模式
- 定义对象间的一种一对多的依赖关系
- 当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新。
适用于:
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
public interface Observer {
public void update();
}
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 has received!");
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println("observer2 has received!");
}
}
public interface Subject {
/*增加观察者*/
public void add(Observer observer);
/*删除观察者*/
public void del(Observer observer);
/*通知所有的观察者*/
public void notifyObservers();
/*自身的操作*/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
//测试方法
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
责任链模式
在责任链模式里,很多的对象由每一个对象对其下 家的引用而联接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
public interface Handler {
public void operator();
}
public abstract class AbstractHandler {
private Handler handler;
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
}
public class MyHandler extends AbstractHandler implements Handler {
private String name;
public MyHandler(String name) {
this.name = name;
}
@Override
public void operator() {
System.out.println(name+"deal!");
if(getHandler()!=null){
getHandler().operator();
}
}
}
MyHandler h1 = new MyHandler("h1");
MyHandler h2 = new MyHandler("h2");
MyHandler h3 = new MyHandler("h3");
h1.setHandler(h2);
h2.setHandler(h3);
h1.operator();
链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现;同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。