设计模式.md
目录
创建型
1 单例(Singleton)
确保一个类只有一个实例存在,并提供该实例的全局访问点。
使用场景:一个无状态的类(没有静态成员变量)使用单列设计模式节省内存空间。例如计数器计数,线程池,日志类(
Logger Classes),配置类(Configuration Classes),线程共享的资源(Accesing resources in shared mode),工厂(
Factories implemented as Singletons)
- 类结构图
- 实现方式’
(1)将构造方法私有化,使其不能在类的外部通过new关键字实例化该类
(2)在该类内部产生一个唯一的实例化对象,并将其封装为private static类型。
(3)定义一个静态方法返回这个唯一对象。
- 实现1-懒汉式-线程不安全
class Singleton{
private static Singleton uniqueInstance;
private Singleton(){
}
public static Singleton getUniqueInstance(){
if(uniqueInstance==null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
优点:私有的静态变量在uniqueInstance在调用获取实例对象的方法的时候才被创建,叫做延迟实例化。没有用到该类时不会被创建,从而节约资源。
缺点:多线程下不安全,多个线程同时进入if(uniqueInstance==null)时将会创建多个实例化对象。即多个线程同时调用获取实例对象的静态方法时,可能会创建多个实例对象。
- 实现2-饿汉式-线程安全
class Singleton{
private static Singleton uniqueInstance = new Singleton();
private Singleton(){
}
public static Singleton getUniqueInstance(){
return uniqueInstance;
}
}
优点:直接将私有的对象实例化,就不会产生线程安全问题。直接实例化
缺点:直接实例化就没有了延迟实例化带来的节约资源的好处。
- 实现3-懒汉式-线程安全
class Singleton{
private static Singleton uniqueInstance;
private Singleton(){
}
public static synchronized Singleton getUniqueInstance(){
if(uniqueInstance==null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
优点:懒汉式线程不安全发生多线程可以同时进入获取实例对象的方法,只要对这个方法加锁,那么一个时刻就只有一个线程进入该方法,就不会创建多个对象。保留了延迟实例化的节约空间的优点,线程也安全。
缺点:只要有线程进入获取实例的方法,其它线程就必须阻塞,即使实例已经被创建,这会让线程阻塞时间过长,影响性能。
- 实现4-双重校验锁-线程安全
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;
}
}
优点:给获取实例的方法加锁会影响性能,加锁只要对实例化的部分代码加锁就好了。而且只有在对象没有被实例化的时候才加锁。双重校验,第一重校验先判断对象有没有被实例化,如果没有被实例化才进行加锁实例化。第二重校验是可能有多个线程同时进来判断为没有实例化,当前一个线程已经实例化后,后面的线程就不用实例化了。否则,多个线程进入了加锁的代码部分,只是会阻塞,每次实例化还是会执行。
uniqueInstance使用volatile修饰:防止指令重拍,指令重排也可能引发线程不安全。
- 实现5-静态内部类实现
class Singleton{
private static Singleton uniqueInstance;
private Singleton(){
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance(){
return SingletonHolder.INSTANCE;
}
}
优点:当Singleton类加载时,静态内部类没有被加载进内存,当调用获取实例的方法的时候,静态内部类加载进内存,这个时候进行实例化,因为类只加载一次,实例化保证只进行一次。有延迟加载的好处,JVM提供了线程安全的支持。
- 实现6-枚举实现
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public static void main(String[] args) {
// 单例测试
Singleton firstSingleton = Singleton.INSTANCE;
firstSingleton.setObjName("firstName");
System.out.println(firstSingleton.getObjName());
Singleton secondSingleton = Singleton.INSTANCE;
secondSingleton.setObjName("secondName");
System.out.println(firstSingleton.getObjName());
System.out.println(secondSingleton.getObjName());
// 反射获取实例测试
try {
Singleton[] enumConstants = Singleton.class.getEnumConstants();
for (Singleton enumConstant : enumConstants) {
System.out.println(enumConstant.getObjName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
firstName
secondName
secondName
secondName
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。
该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。
2 简单工厂(Simple Factory)
在创建一个对象的时候不向客户暴露内部细节,并提供一个创建对象的通用接口。
简单工厂把实例化的操作单独放到一个类中,实际上就是通过这个通用接口的参数来决定具体由哪个子类来进行实例化。
使用场景:负责创建的对象比较少,不会造成工厂方法太过复杂。
- 类结构图
- 实现
Product接口及其实现类
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
SimpleFactory类
public class SimpleFactory {
public Product createProduct(int type){
if(type == 1){
return new ConcreteProduct1();
}else if(type==2){
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
实现
public class Client {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
//do something with the product
}
实质上就是在工厂类中不同的参数实例化为不同的子类。
优点:可以将客户类和具体实现解耦,客户类不需要知道哪些子类以及应当实例化哪些子类。客户往往有多个,如果不知道使用简单工厂,那么所有的客户类都要知道子类的细节。
缺点:如果子类发生变化,例如增加子类,那么就要修改工厂类,那么所有的客户类都要进行修改。违反了开放封闭原则(扩展开放,修改封闭),严格意义上来说不属于设计模式。
3 工厂方法
定义了一个创建对象的抽象类,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。
使用场景:客户端不需要知道它所需对象的类,工厂容易扩展。
- 类结构图
- 实现
工厂抽象类:
public abstract class Factory {
abstract public Product productMethod();
public void doSomething(){
Product product = productMethod();
//do something with the product
}
}
工厂的具体子类:
public class ConcreteFactory extends Factory {
@Override
public Product productMethod() {
return new ConcreteProduct();
}
}
public class ConcreteFactory1 extends Factory{
@Override
public Product productMethod() {
return new ConcreteProduct1();
}
}
public class ConcreteFactory2 extends Factory {
@Override
public Product productMethod() {
return new ConcreteProduct2();
}
}
具体创建对象在具体的工厂子类中实现,与简单工厂不同的是,简单工厂在一个工厂中可以实例化多个不同的对象,工厂方法是
在不同的工厂类中实例化对象,工厂类都继承至同一个抽象类。
优点:实例化延迟到了子类;隐藏了产品的创建细节;所有工厂类都有一个抽象父类;加入新产品时,无需修改抽象工厂和抽象产品提供的接口,只需要添加一个具体的工厂和具体的产品就可以了。
缺点:增加抽象层定义,增加了系统的理解难度;增加新产品要增加工厂类和产品类,增加了系统复杂度;带来额外的编译和运行开销。
4 抽象工厂
提供一个抽象类,用于创建对象家族。
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。(创建很多个不同的相关联的对象,一起创建出来)
从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。
使用场景:当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。
- 类结构
- 实现
抽象产品类:为每一种产品声明接口,即该产品必须有其中的属性或者方法
具体产品类:定义具体工厂生产的具体产品对象,实现抽象产品接口中申明的业务方法。
抽象工厂类:抽象工厂角色,声明一组创建产品的方法,每一个方法对应生成一种产品
具体工厂类:实现抽象工厂中定义的创建产品的方法,生成一组具体的产品,这些产品构成一个产品品种,每一个产品都位于每个产品的等级结构中
public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
abstract public AbstractProductA createProductA();
abstract public AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
return new ProductB2();
}
}
与工厂方法的区别是,抽象工厂中的一个工厂每次回创建多个对象,且这些对象有关联。
优点:除了工厂方法的优点外,可以在类内部对产品族进行约束,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
缺点:对一个对象家族的扩展比较费力。
5 生成器
封装一个对象的构造过程,并允许按步骤构造。
- 类结构
- 实现
一个简易的StringBuilder 实现,参考了 JDK 1.8 源码。
public class AbstractStringBuilder {
protected char[] value;
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
}
public class StringBuilder extends AbstractStringBuilder {
public StringBuilder() {
super(16);
}
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
public class Client {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
final int count = 26;
for (int i = 0; i < count; i++) {
sb.append((char) ('a' + i));
}
System.out.println(sb.toString());
}
}
abcdefghijklmnopqrstuvwxyz
6 原型模式
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
- 类结构
- 实现
public abstract class Prototype {
abstract Prototype myClone();
}
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("abc");
Prototype clone = prototype.myClone();
System.out.println(clone.toString());
}
}
abc
行为型
1 责任链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
- 类结构
Handler:定义处理请求的接口,并且实现后继链(successor
- 实现
public abstract class Handler {
protected Handler successor;
public Handler(Handler successor) {
this.successor = successor;
}
protected abstract void handleRequest(Request request);
}
public class ConcreteHandler1 extends Handler {
public ConcreteHandler1(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE1) {
System.out.println(request.getName() + " is handle by ConcreteHandler1");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandler2 extends Handler {
public ConcreteHandler2(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE2) {
System.out.println(request.getName() + " is handle by ConcreteHandler2");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
public class Request {
private RequestType type;
private String name;
public Request(RequestType type, String name) {
this.type = type;
this.name = name;
}
public RequestType getType() {
return type;
}
public String getName() {
return name;
}
}
public enum RequestType {
TYPE1, TYPE2
}
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1(null);
Handler handler2 = new ConcreteHandler2(handler1);
Request request1 = new Request(RequestType.TYPE1, "request1");
handler2.handleRequest(request1);
Request request2 = new Request(RequestType.TYPE2, "request2");
handler2.handleRequest(request2);
}
}
request1 is handle by ConcreteHandler1
request2 is handle by ConcreteHandler2
2 命令
命令模式是为了解决命令的请求者和命令的实现者之间的解耦。将命令封装在对象中
- 使用命令来参数化其它对象
- 将命令放入队列中进行排队
- 将命令的操作记录到日志中
- 支持可撤销的操作
- 类结构
- Command:命令
- Receiver:命令接收者,也就是命令真正的执行者
- Invoker:通过它来调用命令
- Client:可以设置命令与命令的接收者
- 实现-设计一个遥控器,可以控制电灯开关
Command
public interface Command {
void execute();
}
Command具体实现类
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
public class Light {
public void on() {
System.out.println("Light is on!");
}
public void off() {
System.out.println("Light is off!");
}
}
Invoker
/**
* 遥控器
*/
public class Invoker {
private Command[] onCommands;
private Command[] offCommands;
private final int slotNum = 7;
public Invoker() {
this.onCommands = new Command[slotNum];
this.offCommands = new Command[slotNum];
}
public void setOnCommand(Command command, int slot) {
onCommands[slot] = command;
}
public void setOffCommand(Command command, int slot) {
offCommands[slot] = command;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
}
public class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
invoker.setOnCommand(lightOnCommand, 0);
invoker.setOffCommand(lightOffCommand, 0);
invoker.onButtonWasPushed(0);
invoker.offButtonWasPushed(0);
}
}
3 解释器
为语言创建解释器,通常由语言的语法和语法分析来定义。
- 类结构
TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。
Context:上下文,包含解释器之外的一些全局信息。
- 实现
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
例如一颗解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。
这里的 Context 指的是 String。
public abstract class Expression {
public abstract boolean interpret(String str);
}
public class TerminalExpression extends Expression {
private String literal = null;
public TerminalExpression(String str) {
literal = str;
}
public boolean interpret(String str) {
StringTokenizer st = new StringTokenizer(str);
while (st.hasMoreTokens()) {
String test = st.nextToken();
if (test.equals(literal)) {
return true;
}
}
return false;
}
}
public class AndExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public AndExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str) {
return expression1.interpret(str) && expression2.interpret(str);
}
}
public class OrExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public OrExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
public boolean interpret(String str) {
return expression1.interpret(str) || expression2.interpret(str);
}
}
public class Client {
/**
* 构建解析树
*/
public static Expression buildInterpreterTree() {
// Literal
Expression terminal1 = new TerminalExpression("A");
Expression terminal2 = new TerminalExpression("B");
Expression terminal3 = new TerminalExpression("C");
Expression terminal4 = new TerminalExpression("D");
// B C
Expression alternation1 = new OrExpression(terminal2, terminal3);
// A Or (B C)
Expression alternation2 = new OrExpression(terminal1, alternation1);
// D And (A Or (B C))
return new AndExpression(terminal4, alternation2);
}
public static void main(String[] args) {
Expression define = buildInterpreterTree();
String context1 = "D A";
String context2 = "A B";
System.out.println(define.interpret(context1));
System.out.println(define.interpret(context2));
}
}
true
false
4 迭代器
提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
- 类结构
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
- Iterator 主要定义了 hasNext() 和 next() 方法。
- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。
- 实现
public interface Aggregate {
Iterator createIterator();
}
public class ConcreteAggregate implements Aggregate {
private Integer[] items;
public ConcreteAggregate() {
items = new Integer[10];
for (int i = 0; i < items.length; i++) {
items[i] = i;
}
}
@Override
public Iterator createIterator() {
return new ConcreteIterator<Integer>(items);
}
}
public interface Iterator<Item> {
Item next();
boolean hasNext();
}
public class ConcreteIterator<Item> implements Iterator {
private Item[] items;
private int position = 0;
public ConcreteIterator(Item[] items) {
this.items = items;
}
@Override
public Object next() {
return items[position++];
}
@Override
public boolean hasNext() {
return position < items.length;
}
}
public class Client {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
Iterator<Integer> iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
5 中介者
集中相关对象之间复杂的沟通和控制方式。
- 类结构
- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
- Colleague:同事,相关对象
6 备忘录
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
- 类结构
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。
7 观察者
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(被称为观察者)
使用场景:常用来实现发布订阅功能。
- 类结构
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。
- 实现
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
Subject(被观察者)抽象类
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObserver();
}
Subject(被观察者)实现类
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObserver();
}
@Override
public void registerObserver(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 interface Observer {
void update(float temp, float humidity, float pressure);
}
观察者实现类(通过被观察者的registerObserver() 方法将其注册到被观察者的观察者列表中)
public class StatisticsDisplay implements Observer {
public StatisticsDisplay(Subject weatherData) {
weatherData.reisterObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure);
}
}
public class CurrentConditionsDisplay implements Observer {
public CurrentConditionsDisplay(Subject weatherData) {
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
System.out.println("CurrentConditionsDisplay.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
8 状态
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
- 类结构
- 实现
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
9 策略
定义一系列的算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
使用场景:很多类仅仅是行为不同,需要使用一个算法的不同实现。
- 类结构
- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
设计一个鸭子,它可以动态地改变叫声。这里的算法是鸭子的叫声行为。
Strategy接口
public interface QuackBehavior {
void quack();
}
Strategy的实现
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("quack!");
}
}
public class Squeak implements QuackBehavior{
@Override
public void quack() {
System.out.println("squeak!");
}
}
context类,使用策略的类
public class Duck {
private QuackBehavior quackBehavior;
public void performQuack() {
if (quackBehavior != null) {
quackBehavior.quack();
}
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
public class Client {
public static void main(String[] args) {
Duck duck = new Duck();
duck.setQuackBehavior(new Squeak());
duck.performQuack();
duck.setQuackBehavior(new Quack());
duck.performQuack();
}
}
squeak!
quack!
10 模板方法
定义算法框架,并将一些步骤的实现延迟到子类。通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
- 类结构
- 实现
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
11 访问者
为一个对象结构(比如组合结构)增加新能力。
- 类结构
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。
12 空对象
使用什么都不做的空对象来代替 NULL。
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
- 类结构
结构型
1 适配器
把一个类接口转换成另一个用户需要的接口(挂羊头卖狗肉)。使用适配器类来实现。
使用场景:系统需要使用现有的类,而这些类的接口不符合系统的需要。需要一个统一的输出接口,而输入端的类型不可预知。
- 类结构
Target:目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
分为两种:类的适配器模式和对象的适配器模式
在类适配器模式中,适配器与适配者之间的关机是继承(或实现)的关系。(需要实现的接口就是我们想要的目标接口)
类适配器:当希望将一个类转换成满足一个新接口的类的时候,可以使用类的适配器模式,创建一个新类继承(或实现)原有的类,实现新的接口。
以充电器作为例子,拥有一个IPhone充电器,适配成一个万能充电器(适配器继承原有充电器,实现万能充电器接口)
在对象适配器模式中,适配器与适配者是关联的关系。
首先,需要一个IPhone充电器(Adaptee)
public class IPhoneCharger {
public void applePhoneCharge(){
System.out.println("The iPhone is charging ...");
}
}
需要对这个充电器进行适配,需要一个适配的接口(target)
public interface ChargeAdapter {
public void phoneCharge();
}
类的适配器,继承原有的类,实现新的接口
public class UniversalCharger extends IPhoneCharger implements ChargeAdapter{
@Override
public void phoneCharge() {
System.out.println("The phone is charging, but which kind of phone it is, who cares ...");
//iphone charging
super.applePhoneCharge();
}
}
测试一下
public class AdapterClassTest {
public static void main(String[] args) {
ChargeAdapter charger = new UniversalCharger();
charger.phoneCharge();
}
}
结果
The phone is charging, but which kind of phone it is, who cares ...
The iPhone is charging ...
理解一下:将IPhone的充电器接口适配成了我们想要的万能充电器接口。
对象适配器:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个包装类,持有原类的一个实例,在包装类的方法中,调用实例的方法就行。
public class UniversalCharger implements ChargeAdapter{
IPhoneCharger iphoneCharger;
public UniversalCharger(IPhoneCharger iphoneCharger){
this.iphoneCharger = iphoneCharger;
}
@Override
public void phoneCharge() {
System.out.println("The phone is charging, but which kind of phone it is, who cares ...");
iphoneCharger.applePhoneCharge();
}
}
理解一下:就是不再继承原有的类,而是持有一个原有类的对象,在适配器类中通过原有类的实例调用原有类的方法。
2 桥接
将抽象与实现分离开来,使它们可以独立变化。
- 类结构
- Abstraction:定义抽象类的接口
- Implementor:定义实现类接口
3 组合
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。
- 类结构
组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。
组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
4 装饰
为对象动态添加新的功能。
装饰模式是一种替代继承的技术,使用对象之间的关联关系取代类的继承关系。引入装饰类,扩充新功能。
主要优点:动态的为对象增加新的功能,继承就做不到这一点。
缺点:会产生过多的相似对象。
应用场景:在不影响其它对象的情况下,以动态透明的方式给单个对象添加指责。比如Java IO就使用了装饰者设计模式。
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
角色:
- 抽象组件
- 具体组件
- 抽象装饰者
- 具体装饰者
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
组件:
public interface Beverage {
double cost();
}
具体组件
public class DarkRoast implements Beverage {
@Override
public double cost() {
return 1;
}
}
public class HouseBlend implements Beverage {
@Override
public double cost() {
return 1;
}
}
装饰者接口
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
}
具体装饰者
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
测试
public class Client {
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
设计原则-开放关闭:对扩展开放,对修改关闭。
5 外观
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
- 类结构
设计原则-最少知识原则。只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
6 享元
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
- 类结构
- Flyweight:享元对象
- IntrinsicState:内部状态,享元对象共享内部状态
- ExtrinsicState:外部状态,每个享元对象的外部状态不同
7 代理模式
代理模式:为其它对象提供一种代理以控制对这个对象的访问。
Subject类,定义了RealSubject和Proxy的共用接口,这样就可以在任何使用了RealSubject的地方都可以使用Proxy了
RealSubject类:定义了Proxy所代表的真实实体。
Proxy类:保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样就可以用来替代实体。实现接口的时候除调用实体的方法外,可以增加新的功能。
代理模式应用:
- 远程代理:控制对远程对象(不同地址空间)的访问,它负责将请求及参数进行编码,并向不同地址空间的对象发送已经编码的请求。
- 虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真是对象。这样就可以达到性能的最优化,比如说你打开一个很大的HTML网页时,里面可能有很多的文字和图片,但你还是可以很快打开它,此时你所看到的时所有的文字,但图片却是一张一张地下载后才能看到。那些未打开地图片框,就是通过虚拟代理来替代了真实地图片,此时代理存储了真实图片地路径和尺寸。
- 安全代理:用来控制真实对象访问时地权限。一般用于对象应该有不同地访问权限的时候。
- 智能代理:是指调用真实对象的时候,代理处理另外一些事。如计算真实对象的引用次数,这样当该对象没有引用时,就可以自动释放它;或当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其它对象不能改变它。它们都是通过代理在访问一个对象时附加一些内务处理。
代理模式其实就是在访问对象时引入一定程度的间歇性,因为这种间歇性,可以附加多种用途。
- 实现
一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容区替换原始图片,直到图片加载完成才将图片显示出来。
Image接口(相当于Subject)
public interface Image {
void showImage();
}
HighResolutionImage类实现Image接口(相当于真实对象RealSubject)
public class HighResolutionImage implements Image {
//图片路径
private URL imageURL;
private long startTime;
//图片高度
private int height;
//图片宽度
private int width;
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public HighResolutionImage(URL imageURL) {
this.imageURL = imageURL;
this.startTime = System.currentTimeMillis();
this.width = 600;
this.height = 600;
}
public boolean isLoad(){
//模拟图片加载,延迟三秒加载完成
long endTime = System.currentTimeMillis();
return endTime-startTime>3000;
}
@Override
public void showImage() {
System.out.println("Real Image: "+imageURL);
}
}
ImageProxy实现Iamge接口(代理对象,相当于Proxy)
ublic class ImageProxy implements Image{
private HighResolutionImage highResolutionImage;
public ImageProxy(HighResolutionImage highResolutionImage) {
this.highResolutionImage = highResolutionImage;
}
@Override
public void showImage() {
while (!highResolutionImage.isLoad()){
try {
System.out.println("Temp Image: "+highResolutionImage.getWidth()+" "+highResolutionImage.getHeight());
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
highResolutionImage.showImage();
}
}
测试
class ImageViewer {
public static void main(String[] args) throws MalformedURLException {
String image = "http://image.jpg";
URL url = new URL(image);
HighResolutionImage highResolutionImage = new HighResolutionImage(url);
ImageProxy imageProxy = new ImageProxy(highResolutionImage);
imageProxy.showImage();
}
}
结果:
Temp Image: 600 600
Temp Image: 600 600
Temp Image: 600 600
。。。
Temp Image: 600 600
Temp Image: 600 600
Temp Image: 600 600
Real Image: http://image.jpg