HeadFirst设计模式笔记整理(持续更新)
第一章
原则1:封装变化
把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响
不需要变化的其他部分
原则2:面向接口
将某些行为或方法放在分开的类中,此类专门提供某行为接口的实现。
原则3:组合
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190211112533148.jpg
![多用组合](https://img-blog.csdnimg.cn/20190210130759775.png
不采用继承,而是将几个类组合起来用。使用组合建立的系统具有较大地弹性,不仅可以将算法族封装成类,更可以在运行时动态的改变行为,只要组合的行为对象符合正确的接口标准。
模式一:策略模式
场景:业务需要,系统对象中有一部分属性或方法会经常变动。如不同英雄可以使用不同的武器,那么他的攻击方法将会动态变化。
定义:将某些常变化的行为封装成算法族,使他们实现同一个接口,然后将该接口类型作为抽象出的很少变化的基类的成员变量,进行组合,使算法的变化独立于使用算法的客户。
示例:
图中有继承自Character抽象类的不同的角色,可以使用不同的武器;不同武器的不同行为被封装成了实现WeaponBehavior接口的不同类(算法族);通过调用setWeapon()方法可以动态的切换武器。
第二章
原则4:松耦合
典例:MVC
模式二:观察者模式
场景:多个不同的对象引用同一个对象(主题对象)的某些属性,当其属性发生变化需相应改变所有引用它的对象。如气象站数据中心数据发生变化,各不同类型的布告板需相应更新显示。
定义:以一种松耦合的方式,定义对象之间的一对多依赖,当一个对象(主题)改变状态时,它的所有依赖者(观察者)都会收到通知并自动更新。
典例:JavaBeans、Swing。
示例:气象系统设计
使用java.util.Observable实现气象站
WeatherData类:
import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() { }
public void measurementsChanged() {
//调用setChanged()方法,标记状态已经改变的事实
setChanged();
//调用两种notifyObservers()方法中的一个:
//notifyObservers() :“拉” ,通知观察者有数据更新,让其自己来获取所需
//notifyObservers(Object arg) :“推送”,将更新后的所有内容发送给所有观察者
notifyObservers();
//notifyObservers()没有传送数据对象,这表示采用的做法是“拉”。
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
/**
*get方法供观察者在接到通知后来获取自己需要的信息
*/
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
CurrentConditionsDisplay类(布告板):
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
/**
*需要一Observable当参数,并将CurrentConditionsDisplay对象登记为观察者
*/
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
/**
*先确定可观察者属于WeatherData类型,然后利用 getter方法
*获取温度和湿度测量值,最后调用display()
*@param Observable 表明发出通知的主题
*@param arg 传入notifyObservers()的数据对象,没有则为空
*/
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
使用观察者模式同时保证以上设计原则的思路:
第三章
原则5:开放-关闭
模式三:装饰者模式
场景:业务中存在多个相似的对象,它们之间存在很少的属性或方法上的差别,需要一种良好的组合方式以避免“类爆炸”。如基本咖啡类型中加入不同的配料后会产生大量不同的新品,同时也存在大、中、小杯等其他属性的差异,最终体现在价格上。
定义:以组合方式向被装饰的组件中整合抽象类(装饰者),动态地为对象添加、修改属性或方法。在扩展功能上,装饰者提供了比继承更有弹性的替代方案。
注:装饰者通常是配合其他类似于工厂或生成器这样的模式创建的,具有较好的封装。后话。
典例:java.io包。java.io类图:
示例:咖啡订单系统
Beverage超类:
/**
*Beverage相当于抽象的Component类
*/
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
//getDescription()已经在此实现了
//cost()必须在子类中实现
public abstract double cost();
}
CondimentDecorator装饰者超类:
/**
* 为了在加上装饰后取代Beverage,CondimentDecorator必须扩展自 Beverage 类。
*/
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
//必须重新实现getDescription()方法,新增描述信息
}
Espresso浓缩咖啡基本类:
public class Espresso extends Beverage {
//通过构造器添加饮料的描述
public Espresso() {
description = "Espresso";
}
//直接返回Espresso的价格$1.99
public double cost() {
return 1.99;
}
}
HouseBlend咖啡:
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
public double cost() {
return .89;
}
}
咖啡订单测试:
public class StarbuzzCoffee {
public static void main(String args[]) {
//订一杯纯Espresso,打印出它的描述与价钱
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+ " $" + beverage.cost());
//DarkRoast咖啡,加双倍摩卡,加Whip
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());
//HouseBlend咖啡,加Soy、Mocha、Whip
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
}
}
第四章
广泛使用的编程习惯:简单工厂(非模式)
场景:披萨店推出的披萨口味跟随市场需要动态变化,导致制作工艺常常变动,我们要求一个店员同时负责披萨的制作、准备、烘焙、切块、打包。几天后,店员不满了,披萨制作工艺如此复杂多变,就不能另找一个厨师来专门负责吗?
定义:将同个类不同子类对象的实例化封装在一个工厂类中,专门负责实例化,从而使业务逻辑不关心实例化的具体实现。
模式四:工厂方法模式
场景:现在披萨店开加盟店了,对于同种披萨,在不同地区的制作方式又存在一些地方特色。让一个厨房负责向所有的分店提供披萨似乎不太好,因而我们允许每个分店有自己的厨房(厨房方式对应上面的简单工厂)。当有客人通过总经销商订购分店的披萨时,只需让分店负责即可。其实,在订购流程中的其他步骤,如准备、烘焙、切块、包装、发货…也可以各具地方特色。
定义:定义了一个创建对象的接口(该接口即工厂方法),由子类决定对象具体的创建方式(简单工厂是由一个专门的工厂类)。工厂方法让类把实例化推迟到子类,以达到松耦合。
示例:披萨加盟店
Creator创建者类:
产品类:
平行的类层级:
原则6:依赖倒置
模式五:抽象工厂模式
场景: 披萨店通过原料工厂提供原料,各地区需要的原料种类相同,只是每种原料具体的制作略有差别。
定义: 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确的指定具体的类。
示例:
披萨店抽象工厂方法与不同实例工厂类中的不同方法实现:
未完待续。。。
个人理解,如有谬误,欢迎指正。
参考文章:
1.Markdown 绘制 UML 图 – PlantUML + Gravizo https://blog.csdn.net/heqiangflytosky/article/details/77050849