文章目录
面向可复用性和可维护性的设计模式
1 创建型模式Creational patterns
1.1 工厂方法模式Factory Method pattern
-
适用范围:
当client不知道/不确定要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。 -
实现方法:
定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。 -
工厂方法模式
- 定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类
- 定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类
-
静态工厂方法
- 定义单独的工厂类,或定义在ADT中
- 使用
static
方法,调用时直接类名.方法名
不用new
- 相比于通过构造器(new)构建对象,可以返回原返回类型的任意子类型
2 结构模式Structural patterns
2.2 适配器模式Adapter
- 适用范围:
将某个类/接口转换为client期望的其他形式。 - 实现方法:
- 增加一个接口,用一个子类实现该接口,
- 在子类中进行委派调用legacy的类方法实现适配器
- client面向接口编程,使用定义的子类,隐藏了legacy的类
2.3 装饰器模式Decorator
-
适用范围:
当有许多子类需要自由组合的时候,Java无法多继承,也不适合顺序继承,因此需要一种机制可以为对象增加不同侧面的特性。 -
实现方法:
- Component:
是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,定义一个对象接口可以给这些对象动态地添加职责 - ConcreteComponent:
具体构件是最核心、最原始、最基本的接口或抽象类的实现,需要装饰的就是它 - Decorator:
装饰角色一般是一个抽象类,在它的属性里必然有一个private变量指向Component抽象构件。 - ConcreteDecoratorA和ConcreteDecoratorB:
具体装饰角色是两个具体的装饰类,要把最核心的、最原始的、最基本的东西装饰成其他东西
- Component:
-
举例:
/** * 抽象构件,接口 */ public interface Componet { /** * 抽象方法 */ public abstract void operate(); }
/** * 具体构件 */ public class ConcreteComponet extends Componet { @Override public void operate() { //具体的实现 System.out.println("do something"); } }
/** * 装饰类 */ public class Decorator extends Componet { private Componet componet = null; /** * 通过构造函数传递被修饰者 * * @param componet */ public Decorator(Componet componet) { this.componet = componet; } /** * 委托给被修者执行 */ @Override public void operate() { this.componet.operate(); } }
/** * 具体的装饰类 */ public class ConcreteDecoratorA extends Decorator { /** * 通过构造函数传递被修饰者 * * @param componet */ public ConcreteDecoratorA(Componet componet) { super(componet); } /** * 定义自己的修饰方法 */ private void method1() { System.out.println("修饰DecoratorA"); } @Override public void operate() { this.method1(); super.operate(); } }
/** * 具体的装饰类 */ public class ConcreteDecoratorB extends Decorator { /** * 通过构造函数传递被修饰者 * * @param componet */ public ConcreteDecoratorB(Componet componet) { super(componet); } /** * 定义自己的修饰方法 */ private void method2() { System.out.println("修饰方法DecoratorB"); } @Override public void operate() { //重写父类的operate方法 this.method2(); super.operate(); } }
public class Client { public static void main(String[] args) { Componet componet = new ConcreteComponet(); //第一次修饰 componet = new ConcreteDecoratorA(componet); //第二次修饰 componet = new ConcreteDecoratorB(componet); //修饰后运行 componet.operate(); } }
-
示例:
3 行为模式
3.1 策略模式Strategy
-
适用范围:
某个方法在运行时需要client根据需要动态切换算法,而不是写死在代码里,这就需要用到策略模式。 -
实现方法:
为不同的实现算法构造抽象接口,利用委托,运行时动态传入client倾向的算法类实例- 抽象策略类:通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口
- 具体策略类:抽象策略类的实现,提供具体的算法
- 环境类(context):持有一个策略类的引用,最终给客户端调用
//定义接口,抽象策略类:
public interface Strategy {
void show();
}
//定义一些具体策略:
//策略A
public class StrategyA implements Strategy {
public void show() {
System.out.println("策略A");
}
}
//策略B
public class StrategyB implements Strategy {
public void show() {
System.out.println("策略B");
}
}
//定义环境类,用于连接用户端和服务端,将用户端选择的策略传递给服务端进行挑选、实现
public class Context {
//持有抽象策略角色的引用
private Strategy strategy;
//有参构造方法,将用户输入的策略作为context的私有字段
public Context(Strategy strategy) {
this.strategy = strategy;
}
//通过委派机制展示具体策略
public void ContextShow(){
strategy.show();
}
}
3.2 模板模式Template Method
-
适用范围:
多个客户端共享相同的算法,但在细节上有所不同,即算法由个性部分和共性部分组成。子类中不应重复公共步骤,但需要重用。 -
实现方法:
- 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
- 模板方法(Template Method):定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分,一般为抽象方法,需要实现类。
- 具体子类:实现抽象类中所定义的方法。
- 抽象类:负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
-
举例:
//定义抽象类 public abstract class CarBuilder { protected abstract void BuildSkeleton(); protected abstract void InstallEngine(); protected abstract void InstallDoor(); //模版方法: public void BuildCar() { BuildSkeleton(); InstallEngine(); InstallDoor(); } } //具体实现: //保时捷 public class PorcheBuilder extends CarBuilder { protected void BuildSkeleton() { System.out.println("Building Porche Skeleton"); } protected void InstallEngine() { System.out.println("Installing Porche Engine"); } protected void InstallDoor() { System.out.println("Installing Porche Door"); } } //甲壳虫 public class BeetleBuilder extends CarBuilder { protected void BuildSkeleton() { System.out.println("Building Beetle Skeleton"); } protected void InstallEngine() { System.out.println("Installing Beetle Engine"); } protected void InstallDoor() { System.out.println("Installing Beetle Door"); } } //客户端代码 public static void main(String[] args) { CarBuilder c = new PorcheBuilder(); c.BuildCar();//直接调用父类方法 c = new BeetleBuilder(); c.BuildCar(); }
3.3 迭代器模式Iterator
-
适用范围:
客户端希望对放入容器/集合类的一组ADT对象进行遍历访问,而无需关心容器的具体类型,也就是说,不管对象被放进哪里,都应该提供同样的遍历方式 -
实现方法:迭代器模式包含以下主要角色:
- 抽象聚合(Abstract Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
- 具体聚合(Concrete Aggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Abstract Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
- 具体迭代器(Concrete Iterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置
-
举例:
//定义迭代器接口,声明hasNext方法和next方法 public interface StudentIterator { boolean hasNext(); Student next(); } //定义具体的迭代器类,重写所有的抽象方法 public class StudentIteratorImpl implements StudentIterator { private List<Student> list; private int position = 0; public StudentIteratorImpl(List<Student> list) { this.list = list; } @Override public boolean hasNext() { return position < list.size(); } @Override public Student next() { Student currentStudent = list.get(position); position ++; return currentStudent; } } //定义抽象容器类,包含添加元素,删除元素,获取迭代 public interface StudentAggregate { void addStudent(Student student); void removeStudent(Student student); StudentIterator getStudentIterator(); } //定义具体的容器类,重写所有方法 public class StudentAggregateImpl implements StudentAggregate { // 创建一个学生列表 private List<Student> list = new ArrayList<Student>(); @Override public void addStudent(Student student) { this.list.add(student); } @Override public void removeStudent(Student student) { this.list.remove(student); } @Override public StudentIterator getStudentIterator() { return new StudentIteratorImpl(list);//返回一个迭代器的实例 } }
3.4 访问者模式Visitor
-
适用范围:
对特定类型的object的特定操作(visit),在运行时将
二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类 -
实现方法:
- 为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下通过delegation接入ADT
-
举例:
//创建访问者接口 public interface Person { void feed(Cat cat); void feed(Dog dog); } //创建不同的具体访问者角色(主人和其他人),都需要实现Person接口 public class Owner implements Person { @Override public void feed(Cat cat) { System.out.println("主人喂食猫"); } @Override public void feed(Dog dog) { System.out.println("主人喂食狗"); } } public class Someone implements Person { @Override public void feed(Cat cat) { System.out.println("其他人喂食猫"); } @Override public void feed(Dog dog) { System.out.println("其他人喂食狗"); } } //定义抽象节点 -- 宠物 public interface Animal { void accept(Person person); } //定义实现Animal接口的具体节点(元素) public class Dog implements Animal { @Override public void accept(Person person) { //将具体的访问功能委托给外部传入的visitor person.feed(this);//这里的访问操作就是feed System.out.println("好好吃,汪汪汪!!!"); } } public class Cat implements Animal { @Override public void accept(Person person) { person.feed(this); System.out.println("好好吃,喵喵喵!!!"); } } //定义对象结构,此案例中就是主人的家 public class Home { private List<Animal> nodeList = new ArrayList<Animal>(); public void action(Person person) { for (Animal node : nodeList) {//需要能够遍历被访问者 node.accept(person);//被访问者被(谁)访问 } } //添加操作 public void add(Animal animal) { nodeList.add(animal); } } //测试 public class Client { public static void main(String[] args) { Home home = new Home(); home.add(new Dog()); home.add(new Cat()); Owner owner = new Owner(); home.action(owner); Someone someone = new Someone(); home.action(someone); } }
-
Strategy vs visitor:
- 二者都是通过delegation建立两个对象的动态联系
- 但是Visitor强调是的外部定义某种对ADT的操作,该操作于ADT自身关系不大(只是访问ADT),故ADT内部只需要开放accept(visitor)即可,client通过它设定visitor操作并在外部调用。
- 而Strategy则强调是对ADT内部某些要实现的功能的相应算法的灵活替换。这些算法是ADT功能的重要组成部分,只不过是delegate到外部strategy类而已。
- 区别:
- visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作)
- strategy则是站在内部ADT的角度,灵活变化对其内部功能的不同配置。
- 二者都是通过delegation建立两个对象的动态联系
4 设计模式的共性与差异
4.1 设计模式的对比:共性样式1
-
Adaptor
-
Template
4.2 设计模式的对比:共性样式2
-
Strategy
-
Iterator
-
Factory Method
-
Visitor