设计模式(一):设计模式简介、策略模式、观察者模式、装饰者模式、工厂模式

一、设计模式入门

1、面向对象基础

  • 抽象
  • 封装
  • 多态
  • 继承

2、面向对象原则

  • 封装变化:找出程序中会变化得方面,然后将其和固定不变的方面相分离
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力
  • 对扩展开放,对修改关闭
  • 依赖倒置原则:要依赖抽象,不要依赖具体类。
  • 只和朋友交谈
  • 别找我,我会找你
  • 类应该只有一个改变的理由

3、策略模式

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

例子:

编写各种各样的鸭子类,定义基本的行为呱呱叫(quack)、游泳(swim)、展示(display)。

设计原则

● 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起

应用此设计原则,将fly的实现提取出来,根据飞行的不同,创建多个飞行类代表每种不同的飞行行为。

● 针对接口编程而不是针对对实现编程

如何实现独立出来的飞行方法呢?定义一个FlyBehavior的接口,具体的飞行类都实现此接口,而在Duck中添加此接口的成员,并实现performFly。

public abstract class Duck {
     
    public FlyBehavior flyBehavior;

    public void performFly() {
     
        flyBehavior.fly();
    }

    public abstract void display();
}

public class FakeDuck extends Duck {
     
    public FakeDuck() {
     
        flyBehavior = new FlyNoWay();
    }

    @Override
    public void display() {
     
        System.out.println("fake duck");
    }
}

public class NormalDuck extends Duck {
     

    public NormalDuck() {
     
        flyBehavior = new FlyWithWings();
    }

    @Override
    public void display() {
     
        System.out.println("normal duck");
    }
}

public interface FlyBehavior {
     
    public void fly();
}

public class FlyNoWay implements FlyBehavior {
     
    @Override
    public void fly() {
     
        System.out.println("can't fly");
    }
}

public class FlyWithWings implements FlyBehavior {
     
    @Override
    public void fly() {
     
        System.out.println("fly with wings");
    }
}

4、要点

  • 良好的OO设计必须具备可复用、可扩充、可维护三个特性
  • 模式被认为是历经验证的OO设计经验
  • 模式不是代码,而是针对设计问题的通用解决方案
  • 大多数模式和原则都着眼于软件变化这个主题
  • 大多数的模式都允许系统局部改变独立于其他部分
  • 我们常把系统中会变化的部分抽取出来

二、观察者模式

1、概念

观察者模式定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。

2、设计原则

  • 为交互对象之间的松耦合设计而努力

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖降到了最低

3、自定义主题和观察者

实现WeatherData类和对应的布告板类,以实现当WeatherData中的数据发生变化时能够及时通知到布告板。

//主题(可观察者)接口
public interface Subject {
     

    public void registerObserver(Observer o);

    public void removeObserver(Observer o);

    public void notifyObserver();
}

//观察者接口
public interface Observer {
     

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

}

//显示接口
public interface DisplayElement {
     

    public void display();

}

//WeatherData实现
public class WeatherData implements Subject {
     

    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
     
        observers = new ArrayList<>();
    }

    //注册观察者
    @Override
    public void registerObserver(Observer o) {
     
        observers.add(o);
    }

    //移除观察者
    @Override
    public void removeObserver(Observer o) {
     
        observers.remove(o);
    }

    //通知观察者数据发生变化
    @Override
    public void notifyObserver() {
     
        for (Observer o : observers) {
     
            o.update(this.temperature, this.humidity, this.pressure);
        }
    }

    //当气象站得到更新观测值时,通知观察者
    public void measurementsChanged(){
     
        notifyObserver();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
     
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    //其他方法
}

//布告板实现示例
public class CurrentConditionsDisplay implements Observer, DisplayElement {
     

    private float temperature;
    private float humidity;
    private Subject weatherData;

    //在构造器中调用weatherData进行注册
    public CurrentConditionsDisplay(Subject weatherData) {
     
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    //主题通知时会调用观察者的该方法
    @Override
    public void update(float temperature, float humidity, float perssure) {
     
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    @Override
    public void display() {
     
        System.out.println("Current Condition is: " + temperature + " F degrees and "
                + humidity +  "% humidity");
    }
}

//测试,可以在创建多个类型的布告板,这里省略
public class WeatherStation {
     

    public static void main(String[] args) {
     
        WeatherData weatherData = new WeatherData();
        //建立三个布告板
        CurrentConditionsDisplay currentCondition1 = new CurrentConditionsDisplay(weatherData);
        CurrentConditionsDisplay currentCondition2 = new CurrentConditionsDisplay(weatherData);
        CurrentConditionsDisplay currentCondition3 = new CurrentConditionsDisplay(weatherData);
        //模拟新的气象测量
        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }

}

//结果
Current Condition is: 80.0 F degrees and 65.0% humidity
Current Condition is: 80.0 F degrees and 65.0% humidity
Current Condition is: 80.0 F degrees and 65.0% humidity
Current Condition is: 78.0 F degrees and 90.0% humidity
Current Condition is: 78.0 F degrees and 90.0% humidity
Current Condition is: 78.0 F degrees and 90.0% humidity

4、Java内置可观察者和观察者

使用java.util.Observablejava.util.Observer实现观察者模式

import java.util.Observable;

//Observable超类代替我们实现观察者的管理
public class WeatherData2 extends Observable {
     

    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData2() {
     }

    //当气象站得到更新观测值时,通知观察者
    public void measurementsChanged(){
     
        //在调用notifyObservers()
        //需要先调用setChanged()来指示状态已经改变
        setChanged();
        //没有使用notifyObservers(Object arg),表示采用“拉”的方式获取数据
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
     
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
     
        return temperature;
    }

    public float getHumidity() {
     
        return humidity;
    }

    public float getPressure() {
     
        return pressure;
    }

    //其他方法
}


import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay2 implements Observer, DisplayElement {
     

    private Observable observable;
    private float temperature;
    private float humidity;

    //在构造器中调用weatherData进行注册
    public CurrentConditionsDisplay2(Observable observable) {
     
        this.observable = observable;
        observable.addObserver(this);
    }

    //主题通知时会调用观察者的该方法
    @Override
    public void update(Observable o, Object arg) {
     
        if(o instanceof WeatherData2) {
     
            WeatherData2 weatherData2 = (WeatherData2) o;
            this.temperature = weatherData2.getTemperature();
            this.humidity = weatherData2.getHumidity();
            display();
        }
    }

    @Override
    public void display() {
     
        System.out.println("Current Condition is: " + temperature + " F degrees and "
                + humidity +  "% humidity");
    }
}

java.util.Observable的黑暗面

  • Observable是一个类。由于java不支持多继承,可观察者不能在继承其他类。
  • setChanged()被定义为了protected,除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象中

5、要点

  • 观察者模式定义了对象之间的一对多的关系
  • 主题(可观察者)用一个共同的接口来更新观察者
  • 观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的具体细节,只知道观察者实现了观察者接口
  • 使用此模式时,你可从被观察者处推(push)或拉(pull)数据(推被认为更正确)
  • 有多个观察者时,不可以依赖特定的通知次序
  • Java提供了多种观察者模式的实现,包括通用的java.util.Observable。但是要注意它带来的一些问题。如果有必要的话,可以实现自己的Observable。

三、装饰者模式

1、概念

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰着提供了比继承更有弹性的代替方法。装饰者可以在所委托被装饰者的行为之前/或之后,加上自己的行为,以达到特定的目的。

2、设计原则

  • 对扩展开放,对修改关闭

3、例子

实现不同种类的调料对不同种类的咖啡进行装饰

//饮料抽象类
public abstract class Beverage {
     

    String description = "Unknown Beverage";

    public String getDescription() {
     
        return description;
    }

    public abstract double cost();
}

//装饰器抽象类(调料)
public abstract class CondimentDecorator extends Beverage {
     

    public abstract String getDescription();

}

//Espresso饮料
public class Espresso extends Beverage {
     

    public Espresso() {
     
        description = "Espresso";
    }

    @Override
    public double cost() {
     
        return 1.99;
    }
}

//HouseBland饮料
public class HouseBland extends Beverage{
     

    public HouseBland() {
     
        description = "HouseBland";
    }

    @Override
    public double cost() {
     
        return 0.89;
    }
}

//Mocha调料
//CondimentDecorator继承自Beverage
public class Mocha extends CondimentDecorator{
     

    //用一个实例记录被装饰者
    private Beverage beverage;

    //把被装饰者通过构造器传递为实例变量中
    public Mocha(Beverage beverage) {
     
        this.beverage = beverage;
    }

    public String getDescription() {
     
        return beverage.getDescription() + ", Mocha";
    }

    public double cost() {
     
        //首先把调用委托给被装饰对象,以计算价钱,然后再加上Mocha的价钱,得到最后结果
        return 0.2 + beverage.cost();
    }
}

//Soy调料
public class Soy extends CondimentDecorator{
     

    //用一个实例记录被装饰者
    private Beverage beverage;

    //把被装饰者通过构造器传递为实例变量中
    public Soy(Beverage beverage) {
     
        this.beverage = beverage;
    }

    public String getDescription() {
     
        return beverage.getDescription() + ", Soy";
    }

    public double cost() {
     
        //首先把调用委托给被装饰对象,以计算价钱,然后再加上Mocha的价钱,得到最后结果
        return 0.15 + beverage.cost();
    }
}

//Whip调料
public class Whip extends CondimentDecorator{
     

    //用一个实例记录被装饰者
    private Beverage beverage;

    //把被装饰者通过构造器传递为实例变量中
    public Whip(Beverage beverage) {
     
        this.beverage = beverage;
    }

    public String getDescription() {
     
        return beverage.getDescription() + ", Whip";
    }

    public double cost() {
     
        //首先把调用委托给被装饰对象,以计算价钱,然后再加上Mocha的价钱,得到最后结果
        return 0.1 + beverage.cost();
    }
}

//测试程序
public class StarbuzzCoffee {
     

    public static void main(String[] args) {
     
        //不进行装饰
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription()
                    + " $" + beverage.cost());

        //使用三层装饰,生成了也被摩卡,豆浆,奶泡深培
        Beverage beverage1 = new HouseBland();
        beverage1 = new Mocha(beverage1);
        beverage1 = new Soy(beverage1);
        beverage1 = new Whip(beverage1);
        System.out.println(beverage1.getDescription()
                + " $" + beverage1.cost());

    }
}

//结果
Espresso $1.99
HouseBland, Mocha, Soy, Whip $1.34

4、装饰 Java.io 类

Java.io 类中也使用了装饰器模式,下面主要几个具体组件和具体装饰器。
设计模式(一):设计模式简介、策略模式、观察者模式、装饰者模式、工厂模式_第1张图片

5、要点

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式
  • 组合和委托可以用于在运行时动态地加上新的行为
  • 除了继承,装饰者模式也可以让我们扩展行为
  • 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或者继承实现)
  • 装饰者可以再被装饰者的行为前面与/后面加上自己的行为,甚至将被装饰者的行为整个替换掉,而达到特定的目的
  • 装饰者会导致设计中出现许多小对象,如果过度使用,会使程序变得很复杂

四、工厂模式

1、设计原则

  • 依赖倒置原则:要依赖抽象,不要依赖具体类。

下面几个指导方针,能避免在OO设计中违反依赖倒置原则:

  • 变量不可以持有具体类的引用
  • 不要让类派生自具体类
  • 不要覆盖基类中以实现的方法

2、简单工厂

简单工厂就是通过传入传入参数的方式,创建不同的对象。(与其说是一种设计模式,不如说是一种编程习惯)

1)例子

//简单工厂
public class SimplePizzaFactory {
     

    public Pizza createPizza(String type) {
     
        Pizza pizza = null;

        if("cheese".equals(type)){
     
            pizza = new CheesePizza();
        } else if("pepperoni".equals(type)){
     
            pizza = new PepperoniPizza();
        } else if ("clam".equals(type)) {
     
            pizza = new ClamPizza();
        } else if ("veggie".equals(type)) {
     
            pizza = new VeggiePizza();
        }
        return pizza;
    }
}

//pizzastore的构造器需要一个工厂作为参数
//而orderPizza()方法,通过简单传入订单类型类使用工厂创建披萨
public class PizzaStore {
     

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory){
     
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
     
        Pizza pizza = factory.createPizza(type);

        //pizza准备,烘烤。切割,装盒
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

3、工厂方法

1)概念

工厂方法模式定义了一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。工厂方法模式能够封装具体类型的实例化。

2)例子

通过继承抽象类PizzaStore,并实现其createPizza抽象方法用于创造不同类型的Pizza,实现构造不同区域的PizzaStore工厂。

//抽象类PizzaStore
public abstract class PizzaStore {
     

    public Pizza orderPizza(String type) {
     

        Pizza pizza = createPizza(type);
        
        //pizza准备,烘烤。切割,装盒
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    //PizzaStore的子类在createPizza处理对象的实例化
    protected abstract Pizza createPizza(String type);
}

//PizzaStore的具体实现
public class NYPizzaStore extends PizzaStore {
     
    @Override
    protected Pizza createPizza(String type) {
     
        if ("cheese".equals(type)) {
     
            return new NYStyleCheesePizza();
        } else if ("veggie".equals(type)) {
     
            return new NYStyleVeggiePizza();
        } else if ("clam".equals(type)) {
     
            return new NYStyleClamPizza();
        } else if ("pepperioni".equals(type)) {
     
            return new NYStylePepperioniPizza();
        } else{
     
            return null;
        }
    }
}

从上面代码中可以看到工厂模式的工厂实例中虽然使用类似简单工厂的形式创建的对象,但是他们在本质上却是不一样的

3)图示

4、抽象工厂模式

1)概念

抽象工厂模式提供了一个接口,用于创造相关或依赖对象的家族(一组相关的产品),而不需要明确指明具体类

2)例子

//抽象工厂接口,其中每个原料都有一个对应的方法创造该原料
public interfacen PizzaIngredientFactory{
     
    
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClams();
    
}

//纽约原料工厂
//为每个区域建造一个原料工厂,并且需要继承自PizzaIngredientFactory,并实现每个方法
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
     
    
    public Dough createDough(){
     
        return new ThinCrustDough();
    }
    
    public Sauce createSauce(){
     
        return new MarinaraSauce();
    }
    
    public Cheese createCheese(){
     
        return new ReggianoCheese();
    }
    
    public Veggies[] createVeggies(){
     
        Veggies[] Veggies = {
     new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
        return Veggies;
    }
    
    public Pepperoni createPepperoni(){
     
        return new SlicedPepperoni();
    }
    
    public Clams createClams(){
     
        return new FreshClams();
    }
}

//其他原料工厂
//...


//抽象Pizza类
public abstract class Pizza {
     
	String name;

	Dough dough;
	Sauce sauce;
	Veggies veggies[];
	Cheese cheese;
	Pepperoni pepperoni;
	Clams clam;

	abstract void prepare();

	void bake() {
     
		System.out.println("Bake for 25 minutes at 350");
	}

	void cut() {
     
		System.out.println("Cutting the pizza into diagonal slices");
	}

	void box() {
     
		System.out.println("Place pizza in official PizzaStore box");
	}

	void setName(String name) {
     
		this.name = name;
	}

	String getName() {
     
		return name;
	}
}

//具体的Pizza类
public class CheesePizza extends Pizza {
     
	PizzaIngredientFactory ingredientFactory;
 
   	//构造函数中传入原料工厂接口,并把这个工厂存储在实例变量中
	public CheesePizza(PizzaIngredientFactory ingredientFactory) {
     
		this.ingredientFactory = ingredientFactory;
	}
 
	void prepare() {
     
		System.out.println("Preparing " + name);
		dough = ingredientFactory.createDough();
		sauce = ingredientFactory.createSauce();
		cheese = ingredientFactory.createCheese();
	}
}


//具体的Pizza类
//...

//在PizzaStore中使用抽象工厂
public class NYPizzaStore extends PizzaStore {
     
 
	protected Pizza createPizza(String item) {
     
		Pizza pizza = null;
		PizzaIngredientFactory ingredientFactory = 
			new NYPizzaIngredientFactory();
 
		if (item.equals("cheese")) {
     
  
			pizza = new CheesePizza(ingredientFactory);
			pizza.setName("New York Style Cheese Pizza");
  
		} else if (item.equals("veggie")) {
     
 
			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("New York Style Veggie Pizza");
 
		} else if (item.equals("clam")) {
     
 
			pizza = new ClamPizza(ingredientFactory);
			pizza.setName("New York Style Clam Pizza");
 
		} else if (item.equals("pepperoni")) {
     

			pizza = new PepperoniPizza(ingredientFactory);
			pizza.setName("New York Style Pepperoni Pizza");
 
		} 
		return pizza;
	}
}

3)图示

5、要点

  • 所有工厂模式都用来封装对象的创建
  • 简单工厂虽然并不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序和具体类解耦
  • 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合
  • 工厂方法允许类将实例化延迟到子类中进行
  • 抽象工厂创建相关的对象家族,而不需要依赖他们的具体类
  • 依赖倒置原则,指导我们避免依赖具体类型,而要尽量以来抽象
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不是针对具体实现编程
  • 如果一个不太会改变的类,那么在代码中直接实例化具体类也可以

你可能感兴趣的:(设计模式,Java,后端,设计模式)