设计模式

1. 策略模式

问题描述:

设计不同种类的鸭子拥有不同的叫声和飞行方式。

设计原则:

封装变化在这里变化的是鸭子叫和飞行的行为方式。

针对接口编程,而不是针对实现编程 变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。

运用这一原则,将叫和飞行的行为抽象出来,实现多种不同的叫和飞行的子类,让子类去实现具体的叫和飞行方式。

多用组合,少用继承 组合也就是 has-a 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。

定义:

定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。


public abstract class Duck {

FlyBehavior flyBehavior;

QuackBehavior quackBehavior;



public Duck(){

}



public void performFly(){

flyBehavior.fly();

}



public void setFlyBehavior(FlyBehavior fb){

flyBehavior = fb;

}



public void performQuack(){

quackBehavior.quack();

}



public void setQuackBehavior(QuackBehavior qb){

quackBehavior = qb;

}

}



public class MallarDuck extends Duck{

public MallarDuck(){

flyBehavior = new FlyWithWings();

quackBehavior = new Quack();

}

}



public interface FlyBehavior {

void fly();

}



public class FlyNoWay implements FlyBehavior{

@Override

public void fly() {

System.out.println("FlyBehavior.FlyNoWay");

}

}



public class FlyWithWings implements FlyBehavior{

@Override

public void fly() {

System.out.println("FlyBehavior.FlyWithWings");

}

}





public interface QuackBehavior {

void quack();

}



public class Quack implements QuackBehavior{

@Override

public void quack() {

System.out.println("QuackBehavior.Quack");

}

}



public class MuteQuack implements QuackBehavior{

@Override

public void quack() {

System.out.println("QuackBehavior.MuteQuack");

}

}



public class Squeak implements QuackBehavior{

@Override

public void quack() {

System.out.println("QuackBehavior.Squeak");

}

}



public class MiniDuckSimulator {

public static void main(String[] args) {

Duck mallarDuck = new MallarDuck();

mallarDuck.performQuack();

mallarDuck.performFly();

mallarDuck.setFlyBehavior(new FlyNoWay());

mallarDuck.performFly();

}

}

// QuackBehavior.Quack

// FlyBehavior.FlyWithWings

// FlyBehavior.FlyNoWay

2. 观察者模式

定义:

定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)成为观察者。

设计原则:

为交互对象之间的松耦合设计而努力 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。由于松耦合的两个对象之间互相依赖程度很低,因此系统具有弹性,能够应对变化。

模式类图:

问题描述:

天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。

解决方案类图:


public interface Subject {

public void resisterObserver(Observer o);

public void removeObserver(Observer o);

public void notifyObserver();

}





import java.util.ArrayList;

import java.util.List;



public class WeatherData implements Subject {

private List<Observer> observers;

private float temperature;

private float humidity;

private float pressure;



public WeatherData() {

observers = new ArrayList<>();

}



@Override

public void resisterObserver(Observer o) {

observers.add(o);

}



@Override

public void removeObserver(Observer o) {

int i = observers.indexOf(o);

if (i >= 0) {

observers.remove(i);

}

}



@Override

public void notifyObserver() {

for (Observer o : observers) {

o.update(temperature, humidity, pressure);

}

}



public void setMeasurements(float temperature, float humidity, float pressure) {

this.temperature = temperature;

this.humidity = humidity;

this.pressure = pressure;

notifyObserver();

}

}



public interface Observer {

public void update(float temp, float humidity, float pressure);

}



public class CurrentConditionsDisplay implements Observer {

private Subject weatherData;



public CurrentConditionsDisplay(Subject weatherData) {

this.weatherData = weatherData;

weatherData.resisterObserver(this);

}



@Override

public void update(float temp, float humidity, float pressure) {

System.out.println("CurrentConditionsDisplay.update:" + temp + " " + humidity + " " + pressure);

}

}



public class StatisticsDisplay implements Observer {

private Subject weatherData;



public StatisticsDisplay(Subject weatherData) {

this.weatherData = weatherData;

weatherData.resisterObserver(this);

}



@Override

public void update(float temp, float humidity, float pressure) {

System.out.println("StatisticsDisplay.update:" + temp + " " + humidity + " " + pressure);

}

}



public class WeatherStation {

public static void main(String[] args) {

WeatherData weatherData = new WeatherData();

CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);

StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);



weatherData.setMeasurements(0, 0, 0);

weatherData.setMeasurements(1, 1, 1);

}

}



// CurrentConditionsDisplay.update:0.0 0.0 0.0

// StatisticsDisplay.update:0.0 0.0 0.0

// CurrentConditionsDisplay.update:1.0 1.0 1.0

// StatisticsDisplay.update:1.0 1.0 1.0

3. 装饰模式

定义:

动态地将责任附加到对象上。在扩展功能上,装饰者提供了比继承更有弹性的替代方案。

模式类图

装饰者和具体组件都继承自组件类型,其中具体组件的方法实现不需要依赖于其它对象,而装饰者拥有一个组件类型对象,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰的对象之外,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件有直接实现而不需要委托给其它对象去处理。

问题描述

设计不同种类的饮料,并且每种饮料可以动态添加新的材料,比如可以添加牛奶。计算一种饮料的价格。

问题解决方案类图

设计原则

类应该对扩展开放,对修改关闭, 也就是添加新功能时不需要修改代码。在本章问题中该原则体现在,在饮料中添加新的材料,而不需要去修改饮料的代码。观察则模式也符合这个原则。不可能所有类都能实现这个原则,应当把该原则应用于设计中最有可能改变的地方。

Java I/O 中的装饰者模式


public interface Beverage {

public double cost();

}



public class HouseBlend implements Beverage{

@Override

public double cost() {

return 1;

}

}



public class DarkRoast implements Beverage{

@Override

public double cost() {

return 1;

}

}



public abstract class CondimentDecorator implements Beverage{

protected Beverage beverage;

}



public class Mocha extends CondimentDecorator {



public Mocha(Beverage beverage) {

this.beverage = beverage;

}



@Override

public double cost() {

return 1 + beverage.cost();

}

}







public class Milk extends CondimentDecorator {



public Milk(Beverage beverage) {

this.beverage = beverage;

}



@Override

public double cost() {

return 1 + beverage.cost();

}

}



public class StartbuzzCoffee {

public static void main(String[] args) {

Beverage beverage = new HouseBlend();

beverage = new Mocha(beverage);

beverage = new Milk(beverage);

System.out.println(beverage.cost());

}

}

// 3.0

4. 工厂模式

简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。

工厂方法模式相当于在简单工厂模式的基础上增加一个抽象工厂,在简单工厂模式下如果增加一个产品,要修改工厂类,不符合开闭原则。在工厂方法下,只需要增加具体工厂和具体产品即可。

抽象工厂,类似于一个集团旗下生产的各种产品的工厂,这些产品是一个产品族。是在工厂方法下的扩展。比如一个产品的界面,可以通过直接改变具体工厂的实例来改变产品的界面风格。

4.1 简单工厂

问题描述

有不同的 Pizza,根据不同的情况用不同的子类实例化一个 Pizza 对象。

定义

简单工厂不是设计模式,更像是一种编程习惯。在实例化一个超类的对象时,可以用它的所有子类来进行实例化,要根据具体需求来决定使用哪个子类。在这种情况下,把实例化的操作放到工厂来中,让工厂类来决定应该用哪个子类来实例化。这样做把客户对象和具体子类的实现解耦,客户对象不再需要知道有哪些子类以及实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节,一旦子类发生改变,例如增加子类,那么所有的客户类都要发生改变。

解决方案类图


public interface Pizza {

public void make();

}



public class CheesePizza implements Pizza{

@Override

public void make() {

System.out.println("CheesePizza");

}

}



public class GreekPizza implements Pizza{

@Override

public void make() {

System.out.println("GreekPizza");

}

}



public class SimplePizzaFactory {

public Pizza createPizza(String type) {

if (type.equals("cheese")) {

return new CheesePizza();

} else if (type.equals("greek")) {

return new GreekPizza();

} else {

throw new UnsupportedOperationException();

}

}

}



public class PizzaStore {

public static void main(String[] args) {

SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();

Pizza pizza = simplePizzaFactory.createPizza("cheese");

pizza.make();

}

}

// CheesePizza

4.2 工厂方法模式

问题描述

每个地区的 Pizza 店虽然种类相同,但是都有自己的风味,需要单独区分。例如,一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。

模式定义

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

模式类图

在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。工厂方法常用在在每个子类都有自己的一组产品类。可以为每个子类创建单独的简单工厂,但是把简单工厂中创建对象的代码放到子类中来可以减少类的数目,因为子类不算是产品类,因此完全可以这么做。

解决方案类图


public interface Pizza {

public void make();

}



public interface PizzaStore {

public Pizza orderPizza(String item);

}



public class NYStyleCheesePizza implements Pizza{

@Override

public void make() {

System.out.println("NYStyleCheesePizza is making..");

}

}



public class NYStyleVeggiePizza implements Pizza {

@Override

public void make() {

System.out.println("NYStyleVeggiePizza is making..");

}

}



public class ChicagoStyleCheesePizza implements Pizza{

@Override

public void make() {

System.out.println("ChicagoStyleCheesePizza is making..");

}

}



public class ChicagoStyleVeggiePizza implements Pizza{

@Override

public void make() {

System.out.println("ChicagoStyleVeggiePizza is making..");

}

}





public class NYPizzaStore implements PizzaStore {

@Override

public Pizza orderPizza(String item) {

Pizza pizza = null;

if (item.equals("cheese")) {

pizza = new NYStyleCheesePizza();

} else if (item.equals("veggie")) {

pizza = new NYStyleVeggiePizza();

} else {

throw new UnsupportedOperationException();

}

pizza.make();

return pizza;

}

}



public class ChicagoPizzaStore implements PizzaStore {

@Override

public Pizza orderPizza(String item) {

Pizza pizza = null;

if (item.equals("cheese")) {

pizza = new ChicagoStyleCheesePizza();

} else if (item.equals("veggie")) {

pizza = new ChicagoStyleVeggiePizza();

} else {

throw new UnsupportedOperationException();

}

pizza.make();

return pizza;

}

}



public class PizzaTestDrive {

public static void main(String[] args) {

PizzaStore nyStore = new NYPizzaStore();

nyStore.orderPizza("cheese");

PizzaStore chicagoStore = new ChicagoPizzaStore();

chicagoStore.orderPizza("cheese");

}

}



// NYStyleCheesePizza is making..

// ChicagoStyleCheesePizza is making..

4.3 抽象工厂模式

设计原则

依赖倒置原则:要依赖抽象,不要依赖具体类。听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 PizzaStore 属于高层组件,但是它依赖于底层组件 Pizza 的具体类。如果依赖的是 Pizza 的抽象类,那么就可以不用关心 Pizza 的具体实现细节。

模式定义

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

模式类图

抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。并且,抽象工厂模式也用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 CreateProductA 和 CreateProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要这两个对象的协作才能完成任务。从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory ,而工厂模式使用了继承。

解决方案类图


// 其实就是把"工厂方法"里面的具体组件又细化了, 可以改变具体产品的风格





// 面图接口

public interface Dough {

public String doughType();

}



public class ThickCrustDough implements Dough{



@Override

public String doughType() {

return "ThickCrustDough";

}

}



public class ThinCrustDough implements Dough {

@Override

public String doughType() {

return "ThinCrustDough";

}

}



// 酱接口

public interface Sauce {

public String sauceType();

}



public class MarinaraSauce implements Sauce {

@Override

public String sauceType() {

return "MarinaraSauce";

}

}



public class PlumTomatoSauce implements Sauce {

@Override

public String sauceType() {

return "PlumTomatoSauce";

}

}



// Pizza 工厂接口

public interface PizzaIngredientFactory {

public Dough createDough();

public Sauce createSauce();

}



// 纽约 Pizza 工厂

public class NYPizzaIngredientFactory implements PizzaIngredientFactory{

@Override

public Dough createDough() {

return new ThickCrustDough();

}



@Override

public Sauce createSauce() {

return new MarinaraSauce();

}

}

// 芝加哥 Pizza 工厂

public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{

@Override

public Dough createDough() {

return new ThinCrustDough();

}



@Override

public Sauce createSauce() {

return new PlumTomatoSauce();

}

}



// 纽约店

public class NYPizzaStore {

private PizzaIngredientFactory ingredientFactory;



public NYPizzaStore() {

ingredientFactory = new NYPizzaIngredientFactory();

}



public void makePizza() {

Dough dough = ingredientFactory.createDough();

Sauce sauce = ingredientFactory.createSauce();

System.out.println(dough.doughType());

System.out.println(sauce.sauceType());

}

}



public class NYPizzaStoreTestDrive {

public static void main(String[] args) {

NYPizzaStore nyPizzaStore = new NYPizzaStore();

nyPizzaStore.makePizza();

}

}



// ThickCrustDough

// MarinaraSauce

5. 单例模式

模式定义

确保一个类只有一个实例,并提供了一个全局访问点。

模式类图

单件模式的 Java 实现用一个私有构造器、一个私有静态变量以及一个公有静态函数,该函数返回私有变量,使得所有通过该函数获取的对象都指向这个唯一的私有静态变量。

5.1 经典实现

以下实现中,私有静态变量被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会创建该私有静态变量,从而节约资源。这个实现在多线程环境下是不安全的,因为多个线程能够同时进入 if(uniqueInstance == null) 内的语句块,那么就会多次实例化 uniqueInstance 私有静态变量。


public class Singleton {



private static Singleton uniqueInstance;



// 私有构造方法,外部只能用get获取实例

private Singleton() {

}



public static Singleton getUniqueInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

}

5.2 线程不安全问题的解决方案一

只需要对 getUniqueInstance() 方法加锁,就能让该方法一次只能一个线程访问,从而避免了对 uniqueInstance 变量进行多次实例化的问题。但是这样有一个问题是一次只能一个线程进入,性能上会有一定的浪费。


public static synchronized Singleton getUniqueInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

5.3 线程不安全问题的解决方案二

不用延迟实例化,采用直接实例化。


private static Singleton uniqueInstance = new Singleton();

5.4 线程不安全问题的解决方案三

考虑第一个解决方案,它是直接对 getUniqueInstance() 方法进行加锁,而实际上只需要对 uniqueInstance = new Singleton(); 这条语句加锁即可。使用两个条件语句来判断 uniqueInstance 是否已经实例化,如果没有实例化才需要加锁。


public class Singleton {



private volatile static Singleton uniqueInstance;



private Singleton() {

}



public static Singleton getUniqueInstance() {

if (uniqueInstance == null) {

synchronized (Singleton.class) {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}

6. 命令模式

问题描述

设计一个遥控器,它有很多按钮,每个按钮可以发起一个命令,让一个家电完成相应操作。有非常多的家电,并且也会增加家电。

模式定义

将命令封装成对象,以便使用不同的命令来参数化其它对象。

模式类图

解决方案类图

Invoker 是遥控器,它可以设置命令,并且调用命令对象的 execute() 方法。Receiver 是电灯,是命令真正的执行者。ConcreteCommand 类组合了一个 Receiver 对象,命令的执行委托给 Receiver 对象来处理,也就是 LightOnCommand 命令的 excute 方法委托给 Light 对象来处理,Light 对象通过调用 on() 方法来完成操作。Invoker 不是 Client 对象,是因为命令的创建不是在 Invoker 中完成的,因此需要额外的 Client 对象来处理这些操作。


public interface Command {

public void execute();

}



public class Light {



public void on() {

System.out.println("Light is on!");

}



public void off() {

System.out.println("Light is off!");

}

}



public class LightOnCommand implements Command{

Light light;



public LightOnCommand(Light light) {

this.light = light;

}



@Override

public void execute() {

light.on();

}

}



/**

* 遥控器类

*/

public class SimpleRemoteControl {

Command slot;



public SimpleRemoteControl() {



}



public void setCommand(Command command) {

this.slot = command;

}



public void buttonWasPressed() {

slot.execute();

}



}



public class RemoteLoader {

public static void main(String[] args) {

// 遥控器

SimpleRemoteControl remote = new SimpleRemoteControl();

// 灯

Light light = new Light();

// 灯的行为 Command(作为插槽)

LightOnCommand lightOnCommand = new LightOnCommand(light);

// 遥控器使用插槽

remote.setCommand(lightOnCommand);

remote.buttonWasPressed();

}

}

// Light is on!

7. 适配器模式

模式定义

将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。

模式类图

有两种适配器模式的实现,一种是对象方式,一种是类方式

对象方式是通过组合的方法,让适配器类(Adapter)拥有一个待适配的对象(Adaptee),从而把相应的处理委托给待适配的对象。

类方式用到多重继承,Adapter 可以看成 Target 和 Adaptee 类型,先把它当成 Adaptee 类型然后实例化一个 Adapter 对象,再把它当成 Target 类型的,这样 Client 就可以把这个对象当成 Target 的对象来处理。

问题描述

让鸭子(Duck)适配火鸡(Turkey),Duck 有 quack() 方法,而 Turkey 只有 gobble() 方法。也就是要让 Turkey 也有 Duck 的 quack() 方法。

解决方案类图


public interface Duck {

public void quack();

public void fly();

}



public interface Turkey {

public void gobble();

public void fly();

}



public class WildTurkey implements Turkey{

@Override

public void gobble() {

System.out.println("gobble!");

}



@Override

public void fly() {

System.out.println("fly!");

}

}

// 适配器 Turkey -> Duck

// implements: 适配的对象

public class TurkeyAdapter implements Duck{

Turkey turkey;



public TurkeyAdapter(Turkey turkey) {

this.turkey = turkey;

}



// 实际上 turkey 不会 quack

@Override

public void quack() {

turkey.gobble();

}



@Override

public void fly() {

turkey.fly();

}

}



public class DuckTestDrive {

public static void main(String[] args) {

Turkey turkey = new WildTurkey();

// 把 Turkey 当作是一只 Duck

Duck duck = new TurkeyAdapter(turkey);

duck.quack();

duck.fly();

}

}

// gobble!

// fly!

8. 外观模式

模式定义

提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

设计原则

最少知识原则:应当使得客户对象所需要交互的对象应当尽可能少。

模式类图

问题描述

家庭影院中有众多电器,当要进行观看电影时需要对很多电器进行操作。要求简化这些操作,使得家庭影院类只提供一个简化的接口,例如提供一个看电影的接口而不用具体操作众多电器。

解决方案类图

9. 模板方法模式

模式定义

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。

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

模式类图

模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去具体实现。

问题描述

冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样。

解决方案类图

设计原则

好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖底层组件,底层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。

钩子

钩子(hock):某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法 templteMethod() 中,如果子类需要它就覆盖默认实现并加上自己的实现。


public abstract class CaffeineBeverage {



final void prepareRecipe(){

boilWater();

brew();

pourInCup();

addCondiments();

}



abstract void brew();



abstract void addCondiments();



void boilWater(){

System.out.println("boilWater");

}



void pourInCup(){

System.out.println("pourInCup");

}

}



public class Coffee extends CaffeineBeverage{

@Override

void brew() {

System.out.println("Coffee.brew");

}



@Override

void addCondiments() {

System.out.println("Coffee.addCondiments");

}

}



public class Tea extends CaffeineBeverage{

@Override

void brew() {

System.out.println("Tea.brew");

}



@Override

void addCondiments() {

System.out.println("Tea.addCondiments");

}

}



public class CaffeineBeverageTestDrive {

public static void main(String[] args) {

CaffeineBeverage caffeineBeverage = new Coffee();

caffeineBeverage.prepareRecipe();

System.out.println("-----------");

caffeineBeverage = new Tea();

caffeineBeverage.prepareRecipe();

}

}



// boilWater

// Coffee.brew

// pourInCup

// Coffee.addCondiments

// -----------

// boilWater

// Tea.brew

// pourInCup

// Tea.addCondiments

10. 迭代器模式

模式定义

 提供一种顺序访问一个聚合对象中的各个元素的方法,而又不暴露其内部的表示。

模式类图

客户类拥有一个聚合对象和迭代器对象,迭代器对象是聚合对象生成的。只需要迭代器定义好移动的操作,就可以让聚合对象能够顺序遍历。


public class Aggregate {



private int[] items;



public Aggregate() {

items = new int[10];

for (int i = 0; i < items.length; i++) {

items[i] = i;

}

}



public Iterator createIterator() {

return new ConcreteIterator(items);

}

}



public interface Iterator {

boolean hasNext();

int next();

}



public class ConcreteIterator implements Iterator {



private int[] items; // 数组

private int position = 0; // 下标



public ConcreteIterator(int[] items) {

this.items = items;

}



@Override

public boolean hasNext() {

return position < items.length;

}



@Override

public int next() {

return items[position++];

}

}



public class Client {

public static void main(String[] args) {

Aggregate aggregate = new Aggregate();

Iterator iterator = aggregate.createIterator();

while(iterator.hasNext()){

System.out.println(iterator.next());

}

}

}



// 0

// 1

// 2

// 3

// 4

// 5

// 6

// 7

// 8

// 9

Java 内置的迭代器

Java 中已经有了 Iterator 接口,在使用 Java 实现时,需要让聚合对象实现 Iterable 接口,该接口有一个 iterator() 方法会返回一个 Iterator 对象。使用 Java 内置的迭代器实现,客户对象可以使用 foreach 循环来遍历聚合对象中的每个元素。


// 注意 Iterable 和 Iterator 的区别



import java.util.Iterator;



public class Aggregate implements Iterable<Integer>{



private int[] items;



public Aggregate() {

items = new int[10];

for (int i = 0; i < items.length; i++) {

items[i] = i;

}

}



@Override

public Iterator<Integer> iterator() { // Iterable 接口的 iterable 方法实现

return new ConcreteIterator(items);

}

}





public class ConcreteIterator implements Iterator<Integer> { // Iterator 实现



private int[] items;

private int position = 0;



public ConcreteIterator(int[] items) {

this.items = items;

}



@Override

public boolean hasNext() {

return position < items.length;

}



@Override

public Integer next() {

return items[position++];

}

}



public class Client {

public static void main(String[] args) {

Aggregate aggregate = new Aggregate();

for (int item : aggregate) { // foreach 循环遍历(作用于 Iterable 对象)

System.out.println(item);

}

}

}

11. 组合模式

模式定义

允许将对象组合成树形结构来表现“整体 / 部分”层次结构。

组合能让客户以一致的方式处理个别对象以及对象组合。

设计原则

一个类应该只有一个引起改变的原因。

模式类图

由于组合(Composite)类拥有一个组件(Component)对象,因此组合对象位于树形结构的中间,它还可以继续操作这个组件对象,并忽略组件对象的具体类型。


public abstract class Component {

protected String name;



public Component(String name) {

this.name = name;

}



abstract public void addChild(Component component);



public void print() {

print(0);

}



abstract protected void print(int level);

}



public class Leaf extends Component {

public Leaf(String name) {

super(name);

}



@Override

public void addChild(Component component) {

throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则 , 这样就不用考虑是叶子节点还是组合节点

}



@Override

protected void print(int level) {

for (int i = 0; i < level; i++) {

System.out.print("--");

}

System.out.println("left:" + name);

}

}



import java.util.ArrayList;

import java.util.List;



public class Composite extends Component {



private List<Component> childs;



public Composite(String name) {

super(name);

childs = new ArrayList<>();

}



@Override

public void addChild(Component component) {

childs.add(component);

}



@Override

protected void print(int level) {

for (int i = 0; i < level; i++) {

System.out.print("--");

}

System.out.println("Composite:" + name);

for (Component component : childs) {

component.print(level + 1);

}

}

}



public class Client {

public static void main(String[] args) {

Composite root = new Composite("root");

Component node1 = new Leaf("1");

Component node2 = new Composite("2");

Component node3 = new Leaf("3");

root.addChild(node1);

root.addChild(node2);

root.addChild(node3);

Component node21 = new Leaf("21");

Component node22 = new Composite("22");

node2.addChild(node21);

node2.addChild(node22);

Component node221 = new Leaf("221");

node22.addChild(node221);

root.print();

}

}



// Composite:root

// --left:1

// --Composite:2

// ----left:21

// ----Composite:22

// ------left:221

// --left:3

12. 状态模式

模式定义

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

状态模式的类图和策略模式一样,并且都是能够动态改变对象的行为。但是状态模式是通过状态对象的状态转移来改变客户对象组合的状态对象,而策略模式是通过客户对象本身的决策来改变组合的策略对象。例如,状态模式下,客户对象委托状态对象进行一个处理操作,那么状态对象有可能发生状态转移,使得客户对象拥有的状态对象发生改变。状态对象组合了客户对象,状态转移是状态对象通过改变客户对象所组合的状态对象实现的。

模式类图

问题描述

糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。

直接解决方案

在糖果机的每个操作函数里面,判断当前的状态,根据不同的状态进行不同的处理,并且发生不同的状态转移。这种解决方案把所有的实现细节都放到客户类,这样在新增状态的时候就要去修改客户类的代码。

使用状态模式的解决方案

状态的转移被移到状态类里面,客户类的每个操作只需要委托给状态类即可,而不需要知道当前是什么状态以及状态时如何进行转移的。


public interface State {

/**

* 投入25 分钱

*/

void insertQuarter();



/**

* 退回25 分钱

*/

void ejectQuarter();



/**

* 转动曲柄

*/

void turnCrank();



/**

* 发放糖果

*/

void dispense();

}



public class HasQuarterState implements State{

private GumballMachine gumballMachine;



public HasQuarterState(GumballMachine gumballMachine){

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You can't insert another quarter");

}



@Override

public void ejectQuarter() {

System.out.println("Quarter returned");

gumballMachine.setState(gumballMachine.getNoQuarterState());

}



@Override

public void turnCrank() {

System.out.println("You turned...");

gumballMachine.setState(gumballMachine.getSoldState());

}



@Override

public void dispense() {

System.out.println("No gumball dispensed");

}

}



public class NoQuarterState implements State {

GumballMachine gumballMachine;



public NoQuarterState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You insert a quarter");

gumballMachine.setState(gumballMachine.getHasQuarterState());

}



@Override

public void ejectQuarter() {

System.out.println("You haven't insert a quarter");

}



@Override

public void turnCrank() {

System.out.println("You turned, but there's no quarter");

}



@Override

public void dispense() {

System.out.println("You need to pay first");

}

}



public class SoldOutState implements State {



GumballMachine gumballMachine;



public SoldOutState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You can't insert a quarter, the machine is sold out");

}



@Override

public void ejectQuarter() {

System.out.println("You can't eject, you haven't inserted a quarter yet");

}



@Override

public void turnCrank() {

System.out.println("You turned, but there are no gumballs");

}



@Override

public void dispense() {

System.out.println("No gumball dispensed");

}

}



public class SoldState implements State {

GumballMachine gumballMachine;



public SoldState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("Please wait, we're already giving you a gumball");

}



@Override

public void ejectQuarter() {

System.out.println("Sorry, you already turned the crank");

}



@Override

public void turnCrank() {

System.out.println("Turning twice doesn't get you another gumball!");

}



@Override

public void dispense() {

gumballMachine.releaseBall();

if(gumballMachine.getCount()>0){

gumballMachine.setState(gumballMachine.getNoQuarterState());

} else{

System.out.println("Oops, out of gumballs");

gumballMachine.setState(gumballMachine.getSoldOutState());

}

}

}



public class GumballMachine {

private State soldOutState;

private State noQuarterState;

private State hasQuarterState;

private State soldState;



private State state;

private int count = 0;



public GumballMachine(int numberGumballs) {

count = numberGumballs;

soldOutState = new SoldOutState(this);

noQuarterState = new NoQuarterState(this);

hasQuarterState = new HasQuarterState(this);

soldState = new SoldState(this);



if (numberGumballs > 0) {

state = noQuarterState;

} else {

state = soldOutState;

}

}



public void insertQuarter() {

state.insertQuarter();

}



public void ejectQuarter() {

state.ejectQuarter();

}



public void turnCrank() {

state.turnCrank();

state.dispense();

}



public void setState(State state) {

this.state = state;

}



public void releaseBall() {

System.out.println("A gumball comes rolling out the slot...");

if (count != 0) {

count -= 1;

}

}



public State getSoldOutState() {

return soldOutState;

}



public State getNoQuarterState() {

return noQuarterState;

}



public State getHasQuarterState() {

return hasQuarterState;

}



public State getSoldState() {

return soldState;

}



public int getCount() {

return count;

}

}



public class GumballMachineTestDrive {

public static void main(String[] args) {

GumballMachine gumballMachine = new GumballMachine(5);



gumballMachine.insertQuarter();

gumballMachine.turnCrank();



gumballMachine.insertQuarter();

gumballMachine.ejectQuarter();

gumballMachine.turnCrank();



gumballMachine.insertQuarter();

gumballMachine.turnCrank();

gumballMachine.insertQuarter();

gumballMachine.turnCrank();

gumballMachine.ejectQuarter();



gumballMachine.insertQuarter();

gumballMachine.insertQuarter();

gumballMachine.turnCrank();

gumballMachine.insertQuarter();

gumballMachine.turnCrank();

gumballMachine.insertQuarter();

gumballMachine.turnCrank();

}

}



// You insert a quarter

// You turned...

// A gumball comes rolling out the slot...

// You insert a quarter

// Quarter returned

// You turned, but there's no quarter

// You need to pay first

// You insert a quarter

// You turned...

// A gumball comes rolling out the slot...

// You insert a quarter

// You turned...

// A gumball comes rolling out the slot...

// You haven't insert a quarter

// You insert a quarter

// You can't insert another quarter

// You turned...

// A gumball comes rolling out the slot...

// You insert a quarter

// You turned...

// A gumball comes rolling out the slot...

// Oops, out of gumballs

// You can't insert a quarter, the machine is sold out

// You turned, but there are no gumballs

// No gumball dispensed