设计模式

学习任务

  • 单例
  • 工厂
  • 代理
  • 策略
  • 模板方法
  • 观察者
  • 装饰者
  • 适配器
  • 责任链
  • 建造者

一、单例模式

4.1 定义

单例模式:确保一个类最多只有一个实例,并提供一个全局访问点。

即类的对象当且只有一个:例如缓存、线程池、Spring中的Bean对象

4.2 经典单例模式

通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。

所以实现单例模式,就要将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

单例模式的结构图

public class Singleton {

//通过添加static,来实现将Singleton作为一个类对象来创建 即外部可以Singleton.getInstance()
private static Singleton uniqeInstance=null;

//构造函数设置为 私有的
private Singleton(){

};

public static Singleton getInstance()
{
if(uniqeInstance==null)
{
uniqeInstance=new Singleton();
}
return uniqeInstance;

}

}

4.3 优化多线程问题

当出现多线程问题时,原先的经典单例模式就不再适用。

当两个线程同时操作一个单例对象时,即同时创建uniqeInstance时。若其中一个线程,时间片只能让他执行到if(uniqeInstance==null),然后切换下个线程执行完getInstance(),所以两个线程就获得了两个对象,单例模式就失效了。

解决方法:

1. 线程同步

最简单方式,加个synchronized关键字

public static synchronized Singleton getInstance()

但是如若getInstance()经常调用,则消耗资源增加,效率降低。

2. “饿汉式”创建实例

直接在类创建时实例化私有变量uniqeInstance

public class Singleton {

private static Singleton uniqeInstance = new Singleton();

private Singleton() {};

public static Singleton getInstance() {
return uniqeInstance;
}
}

但是还是存在浪费内存资源的可能,即本次执行可能不需要单例对象但它还是实例化出来了。

3. 双重检查加锁

频繁的调用getInstance(),它也不会每次都进入同步区域。

private static Singleton uniqeInstance;

private Singleton() {};

public static Singleton getInstance() {
// 不会频繁的进入线程同步区域
if (uniqeInstance == null) {
synchronized (Singleton.class) {
if (uniqeInstance == null) {
uniqeInstance = new Singleton();
}
}
}
return uniqeInstance;
}

二、工厂模式

2.1 定义

工厂模式一般分为三种:简单工厂模式、工厂方法模式、抽象工厂模式

(1)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

(2)工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

(3)抽象工厂模式(重点):定义了一个接口用于创建相关或有依赖关系的对象族,而无需明确指定具体类。

2.2 OO原则的不足

我们以卖不同种类的披萨为例,来说明:

public class OrderPizza {

public OrderPizza() {
//披萨是个抽象类,后面根据顾客选用的种类,来进行实例化
Pizza pizza = null;
String ordertype;

do {
ordertype = gettype();

if (ordertype.equals("cheese")) {
//进行实例化
pizza = new CheesePizza();
} else if (ordertype.equals("greek")) {
//进行实例化
pizza = new GreekPizza();
} else if (ordertype.equals("pepper")) {
//....
pizza = new PepperPizza();
} else if (ordertype.equals("chinese")) {
pizza = new ChinesePizza();
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}

private String gettype() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(
System.in));
System.out.println("input pizza type:");
String str = strin.readLine();

return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}

}

这里就出现了问题,new不同种类的披萨(对象的实例化)对项目扩展有影响,主项目过程需要拓展披萨种类时,就必须要重新编译、加载、部署。

2.3 工厂模式解决

根据上述原因,主项目过程需要拓展披萨种类时,我们把对象实例化滴代码抽取处理,用工厂来实现。

  • 简单工厂模式实现

设计方案:定义一个实例化披萨对象的类,封装创建对象的代码

pizza = mSimplePizzaFactory.CreatePizza(ordertype);

public class OrderPizza {
SimplePizzaFactory mSimplePizzaFactory;

public OrderPizza(SimplePizzaFactory mSimplePizzaFactory) {
setFactory(mSimplePizzaFactory);
}

public void setFactory(SimplePizzaFactory mSimplePizzaFactory) {
Pizza pizza = null;
String ordertype;

this.mSimplePizzaFactory = mSimplePizzaFactory;

do {
ordertype = gettype();
pizza = mSimplePizzaFactory.CreatePizza(ordertype);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}

} while (true);

}
......
}

public class SimplePizzaFactory {

public Pizza CreatePizza(String ordertype) {
Pizza pizza = null;

if (ordertype.equals("cheese")) {
pizza = new CheesePizza();
} else if (ordertype.equals("greek")) {
pizza = new GreekPizza();
} else if (ordertype.equals("pepper")) {
pizza = new PepperPizza();
}
return pizza;

}

}
  • 工厂方法模式实现

现在需求进行变更,需要讲刚才的工厂进行拓展(开分店),所以原先一个工厂就变得不够用了,所以根据我们的设计原则,工厂在变所以将工厂抽象或者封装成接口处理。

设计方案:将披萨项目里的披萨对象实例化功能抽象成抽象的方法,在不同的工厂进行具体实现

public abstract class OrderPizza {

public OrderPizza() {
Pizza pizza = null;
String ordertype;

do {
ordertype = gettype();
pizza = createPizza(ordertype);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}

abstract Pizza createPizza(String ordertype);

......
}
/***********************************具体实现***********************************/

public class NYOrderPizza extends OrderPizza {

//只需重写这个变化的createPizza方法。
@Override
Pizza createPizza(String ordertype) {
Pizza pizza = null;

if (ordertype.equals("cheese")) {
pizza = new NYCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new NYPepperPizza();
}
return pizza;

}

}

调用时,就直接调用 具体工厂(分店)的来实现。

OrderPizza mOrderPizza = new NYOrderPizza();

  • 抽象工厂模式实现(主要)

根据我们的设计原则,工厂在变所以将工厂抽象或者封装成接口处理,用接口实现更加的减少了耦合性,也是我们的首选。

一个抽象工厂:

public interface AbsFactory {
public Pizza CreatePizza(String ordertype) ;
}

在主项目里,用接口来实现多态:

public class OrderPizza {
AbsFactory mFactory;

public OrderPizza(AbsFactory mFactory) {
setFactory(mFactory);
}

public void setFactory(AbsFactory mFactory) {
Pizza pizza = null;
String ordertype;
this.mFactory = mFactory;
do {
ordertype = gettype();
pizza = mFactory.CreatePizza(ordertype);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}

} while (true);

}
......
}

/*********************************具体接口实现*********************************/
public class LDFactory implements AbsFactory {

@Override
public Pizza CreatePizza(String ordertype) {
Pizza pizza = null;

if (ordertype.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;

}

}

所以调用时,就直接将具体工厂传入主项目。

OrderPizza mOrderPizza = new OrderPizza(new LDFactory());

2.4 依赖抽象的原则

我们说明依赖抽象的原则:

  • 变量不要持有具体类的引用
  • 不要让类继承自具体类,要继承抽象类或接口(接口最好,程序清晰,代码复用降低)
  • 不要覆盖基类中已实现的方法(一般为通用的方法才进行实现)

三、代理模式

3.1 定义

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。

被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象,以及最重要滴就是代理加强。

代理模式有很多变体,都是为了控制与管理对象访问。

3.2 JavaRMI

RMI:远程方法调用计算机之间通过网络实现对象调用的一种通讯机制

一台计算机上的对象可以调用另外 一台计算机上的对象来获取远程数据。

  • 过去是用TCP/IP来进行远程通讯的主要手段,socket编程来开发。
  • 现在的RPC(远程过程调用)面对复杂的信息传讯时还不太行,每次都去使用底层就不太行。
  • RMI被设计成一种面向对象开发方式,允许程序员使用远程对象来实现通信(底层已经封装好)。

3.3 通过RMI实现

示例项目类图结构:

远程接口和糖果机、以及注册服务

// 远端的接口文件(糖果机),里面是想实现的方法
public interface CandyMachineRemote extends Remote{
public String getLocation() throws RemoteException;
public int getCount() throws RemoteException;
// State是一个自定义对象,所以要进行序列化(网络传输时是通过序列化成二进制文件进行序列化)
public State getstate() throws RemoteException;
}
// 远程糖果机实现
public class CandyMachine extends UnicastRemoteObject implements CandyMachineRemote{

State mSoldOutState;
State mOnReadyState;
State mHasCoin;
State mSoldState;
State mWinnerState;
private String location="";
private State state;
private int count = 0;

public CandyMachine(String location,int count) throws RemoteException{
this.location=location;
this.count = count;
mSoldOutState = new SoldOutState(this);
mOnReadyState = new OnReadyState(this);
mHasCoin = new HasCoin(this);
mSoldState = new SoldState(this);
mWinnerState = new WinnerState(this);
if (count > 0) {
state = mOnReadyState;
} else {
state = mSoldOutState;
}
}
// get、set方法
// 。。。。。。
public void insertCoin() {
state.insertCoin();
}

public void returnCoin() {
state.returnCoin();
}

public void turnCrank() {
state.turnCrank();
state.dispense();
}

void releaseCandy() {
// TODO Auto-generated method stub
if (count > 0) {
count = count - 1;
System.out.println("a candy rolling out!");
}

}

public void printstate() {
state.printstate();
}

}

//远程注册服务
public class RemoteMainTest {
public static void main(String[] args) {

try {
CandyMachine service = new CandyMachine("test1", 7);
// LocateRegistry.createRegistry(6602);
// 注册服务
Naming.rebind("rmi://127.0.0.1:6602/test1", service);
service.insertCoin();
service = new CandyMachine("test2", 5);
// 注册服务
Naming.rebind("rmi://127.0.0.1:6602/test2", service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.toString());
}

}
}

本地调用

public class Monitor {
// 里面是 代理糖果机
private ArrayList<CandyMachineRemote> candyMachinelst;

public Monitor() {
candyMachinelst = new ArrayList<CandyMachineRemote>();
}

public void addMachine(CandyMachineRemote mCandyMachine) {
candyMachinelst.add(mCandyMachine);
}

public void report() {
CandyMachineRemote mCandyMachine;
for (int i = 0, len = candyMachinelst.size(); i < len; i++) {
mCandyMachine = candyMachinelst.get(i);
// 调用糖果机接口
try {
System.out
.println("Machine Loc:" + mCandyMachine.getLocation());

System.out.println("Machine Candy count:"
+ mCandyMachine.getCount());
System.out.println("Machine State:"
+ mCandyMachine.getstate().getstatename());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

}

public class MainTest {

public static void main(String[] args) {
Monitor mMonitor = new Monitor();

try {
// RMI服务进行查找
CandyMachineRemote mCandyMachine = (CandyMachineRemote) Naming
.lookup("rmi://127.0.0.1:6602/test1");
// 加入远程服务
mMonitor.addMachine(mCandyMachine);

mCandyMachine = (CandyMachineRemote) Naming
.lookup("rmi://127.0.0.1:6602/test2");
mMonitor.addMachine(mCandyMachine);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

mMonitor.report();
}

}

总结:Monitor 就像调用本地方法一样,直接调用report()来进行操作。而实际是操作的被代理对象,底层是进行远程调用滴。

3.4 常见的代理模式

常见的代理模式主要为:虚拟代理、动态代理、保护代理、变体。。。我们重点看动态代理。

虚拟代理:为创建开销大的对象提供代理服务,真正对象创建前后进行展示。

3.4.1 动态代理

动态代理:运行时动态的创建代理类对象,并将方法调用转发到指定类。SpringAOP

实际的对象RealSubject里面有req()方法,在用户访问前用动态代理进行控制,管理。通过Proxy和InvocationHandler来控制管理这个实际对象。

3.5 代理模式和装饰者模式区别

  • 装饰者模式,装饰以后会添加新功能
  • 代理模式目的是对目标对象访问的控制和管理

四、策略模式

4.1 定义

策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。

设计原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立于算法的使用者。

4.2 OO原则的不足

继承的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。影响会有溢出效应

如果又要新增需求,使用OO原则(继承实现),只能是每个子类都需要填坑(不变化的都要复写),增加了工作量。

//超类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2)。不是好的设计方式
public class StoneDuck extends Duck {
.... };

4.3 策略模式解决

需要新的设计方式,应对项目的扩展性,降低复杂度。分析项目变化与不变部分,提取变化部分,抽象成接口+实现;不变的部分,写在抽象类(父类)中,即可。

好处:新增行为简单,行为类更好的复用,组合更方便。既有继承带来的复用好处,没有挖坑

接口:
public interface FlyBehavior {
void fly();
}
public class BadFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("--BadFly--");
}
}

public class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("--GoodFly--");
}
}
/*************************************************************************************************/
//“叫”接口同理
public interface QuackBehavior {
void quack();
};

public class BadQuackBehavior implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("--BadQuack--");
}
}

public class GoodQuackBehavior implements QuackBehavior {
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("--GoodQuack--");
}
}

在外面调用中

public class GreenHeadDuck extends Duck {
public GreenHeadDuck() {
mFlyBehavior = new GoodFlyBehavior();
mQuackBehavior = new BadQuackBehavior();
}
//display方法就是不变的,所以不需要进行抽象成接口
@Override
public void display() {...}
}

五、模板方法模式

5.1 定义

模板方法模式:封装了一个算法步骤,并允许子类为一个或多个步骤方法提供实现。

模板方法可以使子类在不改变算法结构的情况下,重新定义算法结构中的某个步骤。

例如:冲咖啡和冲茶 具有类似的功能,就可以构建出模板。

在Java开发中常见的模仿方法模式:

  • 框架的实现实际就是通过模板实现,根据我们的需求自定义框架
  • 排序算法中Comparable比较模板

5.2 模板方法解决

img

注意: 超类中的模板方法( templateMethod)以及不变的方法(boilWater、pourInCup)都添加了final关键字。

我们知道final关键字是不允许子类修改的,所以这些固定不变的方法和模板就需要使用final来进行固定。

//超类
public abstract class HotDrink {

public final void templateMethod(){
boilWater();
brew();
pourInCup();
addCondiments();
send();
}

protected abstract void addCondiments();
protected abstract void brew();

private final void boilWater() {
System.out.println("一.烧水");
}

private final void pourInCup() {
System.out.println("三.倒入杯中");
}
}

5.3 hook

hook:钩子。可以选的子类可以覆盖父类的方法,即可选择实现或者不实现的方法。

比如,现在有了新需求,客户可以自己选择需不需要添加调料。这个怎么做呢?

//超类
public abstract class HotDrink {

public final void templateMethod(){
boilWater();
brew();
pourInCup();
if (wantCondimentsHook()) {
addCondiments();
} else {
System.out.println("我不加调料!");
}
send();
}

protected abstract void addCondiments();
protected abstract void brew();

public boolan wantCondimentsHook() {
// 默认是要的
return true;
}

private final void boilWater() {
System.out.println("一.烧水");
}

private final void pourInCup() {
System.out.println("三.倒入杯中");
}
}
/******************************带hook的子类**************************************/
public class TeaWithHook extend HotDrink {
@Override
public void addCondiments() {
// TODO Auto-generated method stub
System.out.println("加柠檬");
}

@Override
public void brew() {
// TODO Auto-generated method stub
System.out.println("泡茶");
}

@Override
public boolan wantCondimentsHook() {
// 判断是否加调料,然后来设置返回值
}
}

六、观察者模式

6.1 定义

观察者模式(订阅——发布模式): 对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化。

6.2 OO原则的不足

我们能够想到的就是通过这种传统的耦合方式,来实现通知。

/*******************************************目标****************************************/
public class WeatherData {
//属性
//get/set方法

private CurrentConditions mCurrentConditions; //观察者

public WeatherData(CurrentConditions mCurrentConditions) {
this. mCurrentConditions= mCurrentConditions;
}

public void dataChange() {
mCurrentConditions.update(getTemperature(), getPressure(), getHumidity());
}

public void setData(float mTemperature,float mPressure,float mHumidity) {
this.mTemperatrue=mTemperature;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}
}

/*******************************************观察者****************************************/
public class CurrentConditions {
//属性
//get/set方法

public void update(float mTemperature,float mPressure,float mHumidity)
{
this.mTemperature=mTemperature;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
display();
}

public void display()
{
System.out.println("***Today mTemperature: "+mTemperature+"***");
System.out.println("***Today mPressure: "+mPressure+"***");
System.out.println("***Today mHumidity: "+mHumidity+"***");
}
}

可以看到这样做的话,目标与观察者之间的耦合性极高,并且最重要一点:当进行更新时,由于目标中有观察者类,所以程序会进行反复的编译。

6.3 观察者模式解决

首先还是根据我们分析项目的原则,分析变化与不变部分,提取变化部分,抽象成接口+实现;

//目标
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
//观察者
public interface Observer {
public void update(float mTemperatrue,float mPressure,float mHumidity);
}

来进一步的实现

/*******************************************目标实现类****************************************/
public class WeatherDataSt implements Subject{
//属性
//get/set方法

/* 使用数组集合来进行存储观察者 降低了耦合度*/
private ArrayList<Observer> mObservers;

public WeatherDataSt() {
mObservers=new ArrayList<Observer>();
}

public void dataChange() {
notifyObservers();
}

public void setData(float mTemperatrue,float mPressure,float mHumidity)
{
this.mTemperatrue=mTemperatrue;
this.mPressure=mPressure;
this.mHumidity=mHumidity;
dataChange();
}

@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
mObservers.add(o);
}

@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
if(mObservers.contains(o))
{mObservers.remove(o);}
}

@Override
public void notifyObservers() {
// TODO Auto-generated method stub
for(int i=0,len=mObservers.size();i<len;i++)
{
mObservers.get(i).update(getTemperature(), getPressure(), getHumidity());
}
}
}
/*******************************************观察者实现类****************************************/
public class CurrentConditions implements Observer {
//属性
//get/set方法

@Override
public void update(float mTemperatrue, float mPressure, float mHumidity) {
// TODO Auto-generated method stub
this.mHumidity = mHumidity;
this.mPressure = mPressure;
this.mTemperatrue = mTemperatrue;
display();
}

public void display() {
System.out.println("***Today mTemperatrue:" + mTemperatrue + "***");
System.out.println("***Today mPressure:" + mPressure + "***");
System.out.println("***Today mHumidity:" + mHumidity + "***");

}

}

观察者模式是一种对象行为型模式,其主要优点如下。

  • 降低了目标与观察者之间的耦合关系,实现了松耦合、高内聚,两者之间是抽象耦合关系
  • 目标与观察者之间建立了一套触发机制。

应用场景:

  • 对象间存在一对多的关系,一个对象状态改变会影响其他对象。
  • 当一个模型有两方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 例如前面学习Spring Cloud微服务中的,注册中心Eureka就是通过这种观察者模式实现,还有就是MQ中也有该模式(订阅——发布)。

6.4 Java自带的观察者

1. Observable类

Observable 是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的3个方法。

  1. void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。
  2. void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update。方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
  3. void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。

2. Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,通知观察者得,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。

七、装饰者模式

7.1 定义

装饰者模式: 动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。

7.2 OO原则的不足

我们以咖啡馆订单为例进行演示:咖啡馆有不同的咖啡种类、不同的调料。

直观的想法,肯定是设计一个超类,然后每种咖啡进行扩展,并且不同的调料也进行实现具体类进行扩展,如图所示:

但我们遇到有些问题无法高效解决,出现类爆炸

1)增删调料种类

2)添加多份问题

原因:新功能的引入就会影响原有代码,即一旦某一种发生变化,跟它相关联的都要改动。

7.3 装饰者模式解决

  1. 装饰者模式就像打包一个快递

    1)主体:陶瓷、衣服

    2)包装:报纸填充、塑料泡沫、纸板、木板

本案例就是用 调料来装饰咖啡

  1. 用装饰者模式设计重新设计的方案

  1. 装饰者模式下的订单:例如2份巧克力+一份牛奶的LongBlack,采用递归的方式

// 超类
public abstract class Drink {
public String description=""; // 拓展的子类
private float price=0f;;

public void setDescription(String description)
{
this.description=description;
}
public float getPrice()
{
return price;
}
public void setPrice(float price)
{
this.price=price;
}

public String getDescription()
{
return description+"-"+this.getPrice();
}

public abstract float cost();
}
// 中间层
public class Coffee extends Drink {
@Override
public float cost() {
// TODO Auto-generated method stub
return super.getPrice();
}
}
// 子类(咖啡种类)不影响前面的代码
public class Decaf extends Coffee {
public Decaf()
{
super.setDescription("Decaf");
super.setPrice(3.0f);
}
}
// 装饰者分支
public class Decorator extends Drink {
private Drink Obj;

public Decorator(Drink Obj) {
this.Obj=Obj;
};

@Override
public float cost() {
// 价格就是调料价格 + 种类钱
return super.getPrice() + Obj.cost();
}

@Override
public String getDescription() {
return super.description+"-"+super.getPrice()+"&&"+Obj.getDescription();
}
}
// 具体装饰者
public class Milk extends Decorator {

public Milk(Drink Obj) {
super(Obj);
// TODO Auto-generated constructor stub
super.setDescription("Milk");
super.setPrice(2.0f);
}

}
// 测试用例
public class CoffeeBar {
public static void main(String[] args) {
Drink order;
order=new Decaf();
System.out.println("order1 price:"+order.cost());
System.out.println("order1 desc:"+order.getDescription());

System.out.println("****************");
order=new LongBlack();
// 多份咖啡——每一种为调料(种类)
order=new Milk(order);
order=new Chocolate(order);
order=new Chocolate(order);
// 价格进行叠加
System.out.println("order2 price:"+order.cost());
System.out.println("order2 desc:"+order.getDescription());
}
}

7.4 Java中的装饰者

Java中的I/O实际上就是一个内置的装饰者,它的体系结构:

// 自定义一个装饰者(大写装饰者,全部变成大写进行输入)
// 继承FilterInputStream这个中间层
public class UpperCaseInputStream extends FilterInputStream{

protected UpperCaseInputStream(InputStream in) {
super(in);
// TODO Auto-generated constructor stub
}

public int read() throws IOException {
int c=super.read();
return c==-1?c:Character.toUpperCase((char)(c));
}

public int read(byte[] b,int offset,int len) throws IOException {
int result=super.read(b,offset,len);
for(int i=0;i<result;i++) {
b[i]=(byte)Character.toUpperCase((char)(b[i]));
}
return result;
}
}

// 测试
public class InputTest {
public static void main(String[] args) {
int c;
try {
InputStream in = new UpperCaseInputStream(new BufferedInputStream(
new FileInputStream("F:\\test.txt")));
while((c=in.read())>=0) {
System.out.print((char)c);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

八、适配器模式

8.1 定义

适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容

其实在我们平常生活中很常见,都是应用了这种模式。例如转换头,SpringMVC中的HandlerAdapter(处理器适配器)。

8.2 对象适配器和类适配器

项目示例:火鸡类代替鸭子类给用户,通过适配器模式实现

8.2.1 对象适配器(更倾向于使用)

对象适配器:调用时需要传入火鸡类(被适配者)

/******************************火鸡类******************************/
public class WildTurkey implements Turkey {

@Override
public void gobble() {
// TODO Auto-generated method stub
System.out.println(" Go Go");
}

@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I am flying a short distance");
}

}
/******************************鸭子类******************************/
public class GreenHeadDuck implements Duck {

@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println(" Ga Ga");
}

@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I am flying a long distance");
}

}
/******************************对象适配器******************************/
public class TurkeyAdapter implements Duck {
//组合方式实现
private Turkey turkey;

//调用时需要传入火鸡类(被适配者)
public TurkeyAdapter(Turkey turkey)
{
this.turkey=turkey;
}

@Override
public void quack() {
// TODO Auto-generated method stub
turkey.gobble();
}

@Override
public void fly() {
// TODO Auto-generated method stub
for(int i=0;i<6;i++)
{
turkey.fly();
}
}

}

8.2.2 类适配器

类适配器:通过多重继承(在Java中是继承加实现接口)目标接口 和 被适配者 方式来实现适配

public class TurkeyAdapter2 extends WildTurkey implements Duck {

@Override
public void quack() {
// TODO Auto-generated method stub
super.gobble();
}
@Override
public void fly() {
// TODO Auto-generated method stub
super.fly();
super.fly();
super.fly();
}
}

8.2.3 差异

对象适配器和类适配器使用了不同的方法实现适配,对象适配器使用组合,类适配器使用继承。

更倾向于组合适配器,他是通过组合方式实现,灵活更容易扩展;而类适配器他是通过多继承(在Java中是继承加实现接口)实现,一旦写成就无法扩展给子类做适配器

8.3 Java中从枚举器到迭代器适配

//适配器的缺点就是可能适配不了被适配者的所有方法
//例如:枚举器中无remove方法,则迭代器中remove就无法实现枚举器的方法,只能自己写
public class EnumerationIterator implements Iterator<Object> {

private Enumeration enumeration;

public EnumerationIterator(Enumeration enumeration)
{
this.enumeration= enumeration;
}

@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return enumeration.hasMoreElements();
}

@Override
public Object next() {
// TODO Auto-generated method stub
return enumeration.nextElement();
}

@Override
public void remove() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}

}

8.3 适配器模式优点

  • 从用户的角度看不到被适配者,是解耦的

  • 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法

  • 用户收到反馈结果,感觉只是和目标接口(例如鸭子)交互。

九、责任链

9.1 定义

责任链模式:如果有多个对象都有机会处理请求,责任链可使请求的发送者和接收者解耦,请求沿着责任链传递,直到有一个对象处理了它为止。

9.2 OO设计的不足

我们先引入一个示例吧:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请求项目,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的金额不同,员工必须根据自己项目的金额去找不同的领导批准,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导请假签字等。

用OO模式的设计大概就是,用户根据需求自己选择部门报项目:

这样设计就有问题:

  • 请求的发送者和接收者耦合度太高,多个对象处理一个这样的请求

  • 对象复杂,因为它必须知道接受者的具体信息进行选择

  • 动态地增加或删减处理请求接受者就很麻烦

9.3 责任链模式解决

项目的类结构:

责任链模式:如果有多个对象都有机会处理请求,责任链可使请求的发送者和接收者解耦,请求沿着责任链传递,直到有一个对象处理了它为止。

适用场合:

• 有多个对象可以处理一个请求

• 不明确接收者的情况

• 有序、无序链,线型、树形、环形链

// 抽象的接受者
public abstract class Approver {
// 后序接受者的指针
Approver successor;
String Name;
public Approver(String Name)
{
this.Name=Name;
}
public abstract void ProcessRequest( PurchaseRequest request);
public void SetSuccessor(Approver successor) {
// TODO Auto-generated method stub
this.successor=successor;
}
}
// 具体的接受者
public class GroupApprover extends Approver {

public GroupApprover(String Name) {
super(Name+" GroupLeader");
// TODO Auto-generated constructor stub

}

@Override
public void ProcessRequest(PurchaseRequest request) {
// TODO Auto-generated method stub

// 请求接受的条件 request.GetSum() < 5000
if (request.GetSum() < 5000) {
System.out.println("**This request " + request.GetID()
+ " will be handled by "
+ this.Name + " **");
} else {
successor.ProcessRequest(request);
}
}

}
// 用户发起请求
public class MainTest {

public static void main(String[] args) {

Client mClient=new Client();
Approver GroupLeader=new GroupApprover("Tom");
Approver DepartmentLeader=new DepartmentApprover("Jerry");
Approver VicePresident=new VicePresidentApprover("Kate");
Approver President=new PresidentApprover("Bush");

// 外部处理链式结构
GroupLeader.SetSuccessor(DepartmentLeader);
DepartmentLeader.SetSuccessor(VicePresident);
VicePresident.SetSuccessor(President);
President.SetSuccessor(GroupLeader);

// 发送5000元的请求
PurchaseRequest request = mClient.sendRequst(5000);
// 发给责任链的一端进行开始请求传递
GroupLeader.ProcessRequest(request);
}
}

9.4 优缺点

优点:将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求,对象它无须知道请求处理链的内部结构, 可以动态地增加或删减处理请求的链结构。

缺点:请求从链的开头进行遍历,对性能有一定的损耗,并不保证请求一定被处理(大于阈值)。

十、建造者

10.1 定义

建造者模式:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。

与工厂模式的区别:建造者主要是复杂对象的生成,建造者有多个实现类,而工厂主要是生产一些简单的对象。

指挥者(Director):用户是通过指挥者来生成,在指挥者中不涉及具体产品的信息。指挥者调用建造者对象中的部件构造与装配方法完成复杂对象的创建。

建造者模式的结构图

10.2 建造者模式解决

我们拿制作度假计划为案例来展开:

用户是通过指挥者来生成度假计划,指挥者调用建造者对象中的部件构造与装配方法完成复杂对象的创建

// 指挥者 
public class Director {
private AbsBuilder builder;

// builder是个抽象类,传入时再传入具体的实现类
public Director(AbsBuilder builder)
{
this.builder=builder;
}
public void setBuilder(AbsBuilder builder)
{
this.builder=builder;
}
public void construct()
{
builder.buildvacation();
builder.getVacation().showInfo();
}
}
// vecation复杂对象
public class Vacation {
private ArrayList<VacationDay> mVacationDayLst;
private Date mStDate;
private int mDays = 0;
private VacationDay mVacationDay;

public Vacation(String std) {
mVacationDayLst = new ArrayList<VacationDay>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
mStDate = sdf.parse(std);
mVacationDay = new VacationDay(mStDate);
mVacationDayLst.add(mVacationDay);
mDays++;
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public void setStDate(String std) {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
mStDate = sdf.parse(std);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

public Date getStDate() {

return mStDate;
}

public void addDay() {

mVacationDay = new VacationDay(nextDate(mDays));
mVacationDayLst.add(mVacationDay);
mDays++;
}

public boolean setVacationDay(int i) {
if ((i > 0) && (i < mVacationDayLst.size())) {
mVacationDay = mVacationDayLst.get(i);
return true;
}
mVacationDay = null;
return false;
}

public void setHotel(String mHotels) {
mVacationDay.setHotel(mHotels);
}

public void addTicket(String ticket) {
mVacationDay.addTicket(ticket);
}

public void addEvent(String event) {
mVacationDay.addEvent(event);
}

public void showInfo() {
for (int i = 0, len = mVacationDayLst.size(); i < len; i++) {
System.out.println("** " + (i + 1) + " day**");
System.out.println(mVacationDayLst.get(i).showInfo());

}
}

private Date nextDate(int n) {
Calendar cal = Calendar.getInstance();
cal.setTime(mStDate);
cal.add(Calendar.DATE, n);
return cal.getTime();
}
}

建造者创建复杂对象

// 抽象建造者
public abstract class AbsBuilder {

public Vacation mVacation;

public AbsBuilder(String std) {
mVacation = new Vacation(std);
}
......
public Vacation getVacation() {
return mVacation;
}

}

// 以三天度假方案建造者为例子
public class Builder3d extends AbsBuilder {

public Builder3d(String std) {
// 生成度假对象
super(std);
// TODO Auto-generated constructor stub
}

@Override
public void buildDay(int i) {
// TODO Auto-generated method stub
mVacation.setVacationDay(i);
}
......

@Override
public void buildvacation() {
// TODO Auto-generated method stub
addTicket("Plane Ticket");
addEvent("Fly to Destination");
addEvent("Supper");
addEvent("Dancing");
addHotel("Four Seasons");

mVacation.addDay();
addTicket("Theme Park");
addEvent("Bus to Park");
addEvent("lunch");
addHotel("Four Seasons");

mVacation.addDay();

addTicket("Plane Ticket");
addEvent("City Tour");
addEvent("Fly to Home");

}
}

用户操作

Director mDirector = new Director(new Builder4d("2015-12-29"));
mDirector.construct();

mDirector.setBuilder(new Builder3d("2015-8-30"));
mDirector.construct();

10.3 省略指挥者和抽象生成器类

有时候功能没有那么复杂,具体的builder比较少和对象也只有一种,而且用户也可以定制化建造者。

例如StringBuilder。

return this;返回建造对象,是可以连续进行操作。

例如:builder.addTicket("Plane Ticket").addEvent("Fly to Destination").addEvent("Supper").addHotel("Hilton");

// 省略抽象生成器类直接生成
public class BuilderSelf {
public Vacation mVacation;

public BuilderSelf(String std) {
mVacation = new Vacation(std);
// TODO Auto-generated constructor stub

}

public BuilderSelf addDay() {
// TODO Auto-generated method stub

mVacation.addDay();
return this;
}

public BuilderSelf buildDay(int i) {
// TODO Auto-generated method stub

mVacation.setVacationDay(i);
return this;
}

public BuilderSelf addHotel(String hotel) {
// TODO Auto-generated method stub
mVacation.setHotel(hotel);
return this;
}

public BuilderSelf addTicket(String ticket) {
// TODO Auto-generated method stub
mVacation.addTicket(ticket);
return this;
}

public BuilderSelf addEvent(String event) {
// TODO Auto-generated method stub
mVacation.addEvent(event);
return this;
}

public Vacation getVacation() {

return mVacation;

}
}

用户自己直接调用builder来实现功能

public static void testself() {
BuilderSelf builder = new BuilderSelf("2015-9-29");

builder.addTicket("Plane Ticket").addEvent("Fly to Destination")
.addEvent("Supper").addHotel("Hilton");

builder.addDay().addTicket("Zoo Ticket").addEvent("Bus to Zoo")
.addEvent("Feed animals").addHotel("Home Inn");

builder.addDay();
builder.addTicket("Beach");
builder.addEvent("Swimming");
builder.addHotel("Home inn");

builder.addDay().addTicket("Plane Ticket").addEvent("Fly to Home");
builder.getVacation().showInfo();
}