设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
什么是 GOF(四人帮,全拼 Gang of Four)?
在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。
四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。
- 对接口编程而不是对实现编程。
- 优先使用对象组合而不是继承。
设计模式的使用
设计模式在软件开发中的两个主要用途。
开发人员的共同平台
设计模式提供了一个标准的术语系统,且具体到特定的情景。例如,单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。
最佳的实践
设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。
设计模式的类型
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。
序号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 |
|
2 | 结构型模式 这些模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。 |
|
3 | 行为型模式 这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。 |
|
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 |
|
下面用一个图片来整体描述一下设计模式之间的关系:
设计模式的优点
- 提供了一种共享的设计词汇和概念,使开发人员能够更好地沟通和理解彼此的设计意图。
- 提供了经过验证的解决方案,可以提高软件的可维护性、可复用性和灵活性。
- 促进了代码的重用,避免了重复的设计和实现。
- 通过遵循设计模式,可以减少系统中的错误和问题,提高代码质量。
设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
设计模式
设计模式基本要素 1、模式名称 2、问题 3、解决方案 4、效果
1、简单工厂模式(Simple Factory)
简单工厂模式是一种创建型设计模式,它通过一个单一的工厂类来负责创建一系列相关的对象。这个工厂类根据传入的参数来决定实例化哪一个具体类的实例。客户端在使用时不需要知道具体的类名,只需要提供必要的信息给工厂。
package com.company.simplefactory;
abstract class Product {
public abstract void info();
}
package com.company.simplefactory;
class ProductA extends Product {
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
package com.company.simplefactory;
class ProductB extends Product {
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
package com.company.simplefactory;
class Factory {
public static Product createProduct(String type) {
Product product = null;
switch (type) {
case "A":
product = new ProductA();
break;
case "B":
product = new ProductB();
break;
default:
System.out.println("没有 " + type + " 类型的产品!");
break;
}
return product;
}
}
package com.company.simplefactory;
public class SimpleFactory {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.info();
Product productB = Factory.createProduct("B");
}
}
适用性
1、当对象的创建过程相对集中,并且客户端对于如何创建对象不感兴趣时,简单工厂可以封装这些创建细节。
2、如果产品的种类相对较少,并且不太可能发生变化,简单工厂可以减少系统的复杂性。
3、当希望避免客户端代码直接实例化对象,而是通过一个统一的接口来创建对象时,简单工厂可以提供这样的接口。
4、简单工厂更适合那些创建逻辑比较简单的情况,如果创建过程涉及复杂的逻辑判断或者条件分支,可能需要考虑其他工厂模式,如工厂方法或抽象工厂。
5、当客户端代码不依赖于产品的具体类,而只需要知道产品的接口或抽象类时,简单工厂可以帮助客户端与具体类解耦。
2、工厂方法模式(Factory Method)
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
package com.company.factory;
// class Factory
interface Factory {
public Product createProduct();
}
package com.company.factory;
// abstract class Product
interface Product {
// public abstract void info();
public void info();
}
package com.company.factory;
class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
package com.company.factory;
class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
package com.company.factory;
// class ProductA extends Product
class ProductA implements Product {
@Override
public void info() {
System.out.println("产品的信息:A");
}
}
package com.company.factory;
// class ProductB extends Product
class ProductB implements Product {
@Override
public void info() {
System.out.println("产品的信息:B");
}
}
package com.company.factory;
public class FactoryMethod {
public static void main(String[] args) {
Factory factoryA = new FactoryA();
// 父类 对象名 = new 子类();
Product productA = factoryA.createProduct();
// Product productA = new ProductA();
productA.info();
Factory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
productB.info();
}
}
适用性
Factory Method模式适用于:
1、当一个类不知道它所必须创建的对象的类的时候。
2、当一个类希望由它的子类来指定它所创建的对象的时候。
3、当类创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
3、抽象工厂模式(Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体实现的类。
package com.company.AbstractFactory;
interface ProductA {
public void info();
}
package com.company.AbstractFactory;
class ProductA1 implements ProductA {
@Override
public void info() {
System.out.println("产品的信息:A1");
}
}
package com.company.AbstractFactory;
class ProductA2 implements ProductA {
@Override
public void info() {
System.out.println("产品的信息:A2");
}
}
package com.company.AbstractFactory;
interface ProductB {
public void info();
}
package com.company.AbstractFactory;
class ProductB1 implements ProductB {
@Override
public void info() {
System.out.println("产品的信息:B1");
}
}
package com.company.AbstractFactory;
class ProductB2 implements ProductB {
@Override
public void info() {
System.out.println("产品的信息:B2");
}
}
package com.company.AbstractFactory;
interface Factory {
public ProductA createProductA();
public ProductB createProductB();
}
package com.company.AbstractFactory;
class Factory1 implements Factory {
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
package com.company.AbstractFactory;
class Factory2 implements Factory {
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
package com.company.AbstractFactory;
public class AbstractFactory {
public static void main(String[] args) {
Factory factory1 = new Factory1();
ProductA productA = factory1.createProductA();
productA.info();
Factory factory2 = new Factory2();
ProductB productB = factory2.createProductB();
productB.info();
}
}
适用性:
AbstractFactory 模式仅适用于:
1、一个系统要独立于它的产品的创建、组合和表示是。
2、一个系统要由多个产品系列中的一个来配置。
3、当要强调一系列相关的产品对象的设计以便进行联合使用时。
4、当提供一个产品类库,只想显示它们的接口而不是实现时。
4、原型模式(Prototype)
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
package com.company.Prototype;
interface Prototype {
public Object Clone();
}
package com.company.Prototype;
class Product implements Prototype {
private int id;
private double price;
public Product() {
}
public Product(int id, double price) {
this.id = id;
this.price = price;
}
public int getId() {
return id;
}
public double getPrice() {
return price;
}
@Override
public Object Clone() {
Product object = new Product();
object.id = this.id;
object.price = this.price;
return object;
}
}
package com.company.Prototype;
public class PrototypePattern {
public static void main(String[] args) {
Product product1 = new Product(2022, 5.28);
System.out.println(product1.getId() + " " + product1.getPrice());
// Product product2 = new Product(2022, 5.28);
Product product2 = (Product) product1.Clone();
System.out.println(product2.getId() + " " + product2.getPrice());
Product product3 = (Product) product1.Clone();
System.out.println(product3.getId() + " " + product3.getPrice());
}
}
适用性
Prototype模式适用于:
1、当一个系统应该独立于它的产品创建、构成和表示时。
2、当要实例化的类是在运行时刻指定时,例如,通过动态加载。
3、为了避免创建一个与产品类层次平行的工厂类层次时。
4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。
5、单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
package com.company.Singleton;
class Singleton {
private int number = 2022;
public void setNumber(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
package com.company.Singleton;
public class SingletonPattern {
public static void main(String[] args) {
// Singleton singleton1 = new Singleton();
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
Singleton singleton3 = Singleton.getInstance();
System.out.println(singleton1.getNumber() + " " + singleton2.getNumber() + " " + singleton3.getNumber());
singleton1.setNumber(528);
System.out.println(singleton1.getNumber() + " " + singleton2.getNumber() + " " + singleton3.getNumber());
}
}
适用性
Singleton 模式适用于:
1、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
2、当这个唯一实例应该是通过子类化可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。
6、适配器模式(Adapter)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式是的原本由于接口不兼容而不能一起工作的那个类可以一起工作。
package com.company.Adapter;
class USB {
public void Request() {
System.out.println("USB数据线");
}
}
package com.company.Adapter;
class TypeC {
public void SpecificRequest() {
System.out.println("Type-C数据线");
}
}
package com.company.Adapter;
class Adapter extends USB {
private TypeC typeC = new TypeC();
@Override
public void Request() {
typeC.SpecificRequest();
}
}
package com.company.Adapter;
public class AdapterPattern {
public static void main(String[] args) {
USB usb = new Adapter();
usb.Request();
}
}
适用性
Adapter模式适用于:
1、想使用一个已经存在的类,而它的接口不符合要求。
2、想创建一个可以服用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
3、(仅适用于对象Adapter)想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
7、桥接模式(Bridge)
将抽象的部分与其实现的部分分离,使它们都可以独立的变化。
package com.company.Bridge;
interface Color {
public void OperationImp(String name);
}
package com.company.Bridge;
class Red implements Color {
@Override
public void OperationImp(String name) {
System.out.println(name + ":红色");
}
}
package com.company.Bridge;
class Blue implements Color {
@Override
public void OperationImp(String name) {
System.out.println(name + ":蓝色");
}
}
package com.company.Bridge;
abstract class Product {
private String name;
protected Color color;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setColor(Color color) {
this.color = color;
}
public abstract void Operation();
}
package com.company.Bridge;
class ProductA extends Product {
@Override
public void Operation() {
color.OperationImp(this.getName());
}
}
package com.company.Bridge;
public class BridgePattern {
public static void main(String[] args) {
Product productA1 = new ProductA();
Product productA2 = new ProductA();
Color red = new Red();
productA1.setName("产品A1");
productA1.setColor(red);
productA1.Operation();
Blue blue = new Blue();
productA2.setName("产品A2");
productA2.setColor(blue);
productA2.Operation();
}
}
适用性
Bridge模式适用于:
1、不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如,这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
2、类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是Bridge模式使得开发者可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
3、对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
4、(C++)想对客户完全隐藏抽象的实现部分。
5、有许多类要生成的类层次结构。
6、想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
8、组合模式(Composite)
将对象组合成树型结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
package com.company.CompositePattern;
import java.util.List;
abstract class AbstractFile {
protected String name;
public void printName() {
System.out.println(name);
}
public abstract boolean Add(AbstractFile file);
public abstract boolean Remove(AbstractFile file);
public abstract List<AbstractFile> getChildren();
}
package com.company.CompositePattern;
import java.util.ArrayList;
import java.util.List;
class Folder extends AbstractFile {
private List<AbstractFile> childrenList = new ArrayList<AbstractFile>();
public Folder(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile file) {
return childrenList.add(file);
}
@Override
public boolean Remove(AbstractFile file) {
return childrenList.remove(file);
}
@Override
public List<AbstractFile> getChildren() {
return childrenList;
}
}
package com.company.CompositePattern;
import java.util.List;
class File extends AbstractFile {
public File(String name) {
this.name = name;
}
@Override
public boolean Add(AbstractFile file) {
return false;
}
@Override
public boolean Remove(AbstractFile file) {
return false;
}
@Override
public List<AbstractFile> getChildren() {
return null;
}
}
package com.company.CompositePattern;
import java.util.List;
public class CompositePattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
AbstractFile root = new Folder("root");
AbstractFile folderA = new Folder("folderA");
AbstractFile folderB = new Folder("folderB");
AbstractFile fileC = new File("fileC");
AbstractFile fileD = new File("fileD");
AbstractFile fileE = new File("fileE");
root.Add(folderA);
root.Add(folderB);
root.Add(fileC);
folderA.Add(fileD);
folderA.Add(fileE);
print(root);
}
static void print(AbstractFile file) {
file.printName();
List<AbstractFile> childrenList = file.getChildren();
if (childrenList == null) return;
// for (对象类型 对象名 : 遍历对象)
for (AbstractFile children : childrenList) {
// children.printName();
print(children);
}
}
}
适用性
Composite模型适用于:
1、相表示对象的部分-整体层次结构。
2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
9、装饰器模式(Decorator)
动态地给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更加灵活。
package com.company.DecoratorPattern;
abstract class Person {
protected String name;
public abstract void Operation(); // 职责
}
package com.company.DecoratorPattern;
class Student extends Person {
public Student(String name) {
this.name = name;
}
@Override
public void Operation() {
System.out.print(name + "的职责:学习 ");
}
}
package com.company.DecoratorPattern;
abstract class Decorator extends Person {
protected Person person;
}
package com.company.DecoratorPattern;
class DecoratorA extends Decorator {
public DecoratorA(Person person) {
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.print("写作业 ");
}
}
package com.company.DecoratorPattern;
class DecoratorB extends Decorator {
public DecoratorB(Person person) {
this.person = person;
}
@Override
public void Operation() { // 职责
person.Operation(); // 原本的职责
System.out.print("考试 ");
}
}
package com.company.DecoratorPattern;
public class DecoratorPattern {
public static void main(String[] args) {
Person zhangsan = new Student("张三");
zhangsan = new DecoratorA(zhangsan);
zhangsan = new DecoratorB(zhangsan);
zhangsan.Operation();
System.out.println("\n=====我是分割线=====");
// 对象链
Person lisi = new DecoratorB(new DecoratorA(new Student("李四")));
lisi.Operation();
}
}
适用性
Decorator模式适用于:
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、处理那些可以撤销的职责。
3、当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类。
10、外观模式(Facade)
为子系统中的一组接口提供一个一致的界面。Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
package com.company.FacadePattern;
class SubSystemOne {
public void methodOne() {
System.out.println("执行子系统一的功能");
}
}
package com.company.FacadePattern;
class SubSystemTwo {
public void methodTwo() {
System.out.println("执行子系统二的功能");
}
}
package com.company.FacadePattern;
class SubSystemThree {
public void methodThree() {
System.out.println("执行子系统三的功能");
}
}
package com.company.FacadePattern;
class Facade {
SubSystemOne subSystemOne;
SubSystemTwo subSystemTwo;
SubSystemThree subSystemThree;
public Facade() {
subSystemOne = new SubSystemOne();
subSystemTwo = new SubSystemTwo();
subSystemThree = new SubSystemThree();
}
public void methodA() {
subSystemOne.methodOne();
}
public void methodB() {
subSystemTwo.methodTwo();
}
public void methodC() {
subSystemThree.methodThree();
}
}
package com.company.FacadePattern;
public class FacadePattern {
public static void main(String[] args) {
Facade facade = new Facade();
facade.methodA();
facade.methodB();
facade.methodC();
}
}
适用性
Facade模式适用于:
1、要为一个复杂子系统提供一个简单接口时,子系统往往因为不断演化而变得越来越复杂,大多数模式使用时都会产生更多更小的类,这使得子系统更具有可重用性,也更容易对子系统进行定制,但也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的默认视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制行的用户可以越过Facade层。
2、客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他子系统分离,可以提高子系统的独立性和可移植性。
3、当需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让他们仅通过Facade进行通信,从而简化了它们之间的依赖关系。
11、享元模式(Flyweight)
运用共享技术有效地支持大量细粒度的对象。
package com.company.FlyWeightPattern;
abstract class Shape {
protected String color;
public abstract void draw(int x, int y);
}
package com.company.FlyWeightPattern;
class Circle extends Shape {
public Circle(String color) {
this.color = color;
}
@Override
public void draw(int x, int y) {
System.out.println("draw a color:" + color + " circle x:" + x + " y:" + y);
}
}
package com.company.FlyWeightPattern;
import java.util.HashMap;
import java.util.Map;
class ShapeFactory {
private Map<String, Shape> map = new HashMap<String, Shape>();
public Shape getShape(String key) {
if (!map.containsKey(key)) {
map.put(key, new Circle(key));
System.out.println("create color:" + key + " circle");
}
return map.get(key);
}
}
package com.company.FlyWeightPattern;
import java.util.Random;
public class FlyWeightPattern {
public static void main(String[] args) {
ShapeFactory factory = new ShapeFactory();
Random random = new Random();
String[] colors = {"red", "blue", "green", "white", "black"};
for (int i = 1; i <= 100; i++) {
int x = random.nextInt(colors.length); // [0 ~ 4]
Shape shape = factory.getShape(colors[x]);
System.out.print("第" + i + "个圆:");
shape.draw(random.nextInt(2022), random.nextInt(528));
}
}
}
适用性
Flyweight模式适用于:
1、一个应用程序使用了大量的对象。
2、完全由于使用大量的对象,造成很大的存储开销。
3、对象的大多数状态都可变为外部状态。
4、如果删除对象的外部状态,那么可以用相对教授的共享对象取代很多组对象。
5、用用程序不依赖于对象标识。由于Flyweight对象可以被共享,所以对于概念上明显有别的对象,标识测试将返回真值。
12、代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
package com.company.ProxyPattern;
interface Subject {
public void buy();
}
package com.company.ProxyPattern;
class RealSubject implements Subject {
@Override
public void buy() {
System.out.println("付钱");
}
}
class Proxy implements Subject {
protected RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void buy() {
System.out.println("办理购买前的手续");
realSubject.buy(); // 付钱
System.out.println("办理购买后的手续");
}
}
package com.company.ProxyPattern;
public class ProxyPattern {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.buy();
}
}
适用性:
Proxy模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候,常见情况有:
1、远程代理(Remote Proxy)为一个对象在不同地址空间提供局部代表。
2、虚代理(Virtual Proxy)根据需要创建开销很大的对象。
3、保护代理(Protection Proxy)控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
4、智能引用(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以被自动释放;当第一次引用一个持久对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
13、责任链模式(Chain of Responsibility)
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
package com.company.ChainOfResponsibilityPattern;
abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public abstract void HandlerRequest(int request);
}
package com.company.ChainOfResponsibilityPattern;
class FuDaoYuan extends Handler { // <= 7 审批
@Override
public void HandlerRequest(int request) {
if (request <= 7) {
System.out.println("辅导员审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
package com.company.ChainOfResponsibilityPattern;
class YuanZhang extends Handler { // <= 15 审批
@Override
public void HandlerRequest(int request) {
if (request <= 15) {
System.out.println("院长审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
package com.company.ChainOfResponsibilityPattern;
class XiaoZhang extends Handler { // <= 30 审批
@Override
public void HandlerRequest(int request) {
if (request <= 30) {
System.out.println("校长审批通过");
} else {
if (next != null) {
next.HandlerRequest(request);
} else {
System.out.println("无法审批");
}
}
}
}
package com.company.ChainOfResponsibilityPattern;
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
Handler fudaoyuan = new FuDaoYuan();
Handler yuanzhang = new YuanZhang();
Handler xiaozhang = new XiaoZhang();
fudaoyuan.setNext(yuanzhang);
yuanzhang.setNext(xiaozhang);
fudaoyuan.HandlerRequest(31);
}
}
适用性
Chain of Responsibility模式适用于以下条件:
1、有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2、想在不明确指定接收者的情况下向多个对象中的一个提交一个请求。
3、可处理一个请求的对象集合应被动态指定。
14、命令模式(Command)
将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
package com.company.CommandPattern;
class Tv { // 接收者 电视机
public void OnAction() { // 开机行为
System.out.println("电视机开机了...");
}
public void OffAction() { // 关机行为
System.out.println("电视机关机了...");
}
}
package com.company.CommandPattern;
interface Command { // 命令接口
public void Execute(); // 执行命令
}
package com.company.CommandPattern;
class Invoker { // 请求者
private Command command; // 命令
public void setCommand(Command command) { // 设置请求者 的 请求的命令
this.command = command;
}
public void call() { // 调用
command.Execute();
}
}
package com.company.CommandPattern;
class OnCommand implements Command { // 开机命令
private Tv tv;
public OnCommand(Tv tv) {
this.tv = tv;
}
@Override
public void Execute() {
tv.OnAction();
}
}
package com.company.CommandPattern;
class OffCommand implements Command { // 关机命令
private Tv tv;
public OffCommand(Tv tv) {
this.tv = tv;
}
@Override
public void Execute() {
tv.OffAction();
}
}
package com.company.CommandPattern;
public class CommandPattern {
public static void main(String[] args) {
Tv tv = new Tv(); // 接收者 对象 电视机
Command onCommand = new OnCommand(tv); // 命令对象 开机命令
Command offCommand = new OffCommand(tv); // 命令对象 关机命令
Invoker invoker = new Invoker(); // 请求者
invoker.setCommand(onCommand); // 给请求者设置 开机 命令
invoker.call(); // 请求者去请求命令
System.out.println("========================================");
invoker.setCommand(offCommand); // 给请求者设置 关机命令
invoker.call(); // 请求者去请求命令
}
}
适用性
Command模式适用于:
1、抽象出待执行的动作以参数化某对象。Command模式是过程语言中的回调(Callback)机制的一个面向对象的替代品。
2、在不同的时刻指定、配列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可以将负责该请求的命令对象传递给另一个不同的进程并在那儿实现该请求。
3、支持取消操作。Command的Execute操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
4、支持修改日志。这样当系统奔溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从奔溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
5、用构建在原语操作上的高层操作构造一个系统。这样一中结构在支持事务(Transaction)的信息系统中很常见。Command模式提供了对事务进行建模的方法。Command有一个公共接口,使得可以用同一种方式调用所有的事务,同时使用该模式也易于添加新事务以扩展系统。
15、解释器模式(Interpreter)
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
package com.company.InterpreterPattern;
interface Expression {
public boolean Interpret(String info);
}
package com.company.InterpreterPattern;
import java.util.HashSet;
import java.util.Set;
class TerminalExpression implements Expression {
private Set<String> set = new HashSet<>();
public TerminalExpression(String[] data) {
// for (遍历对象类型 对象名 : 遍历对象)
for (String str : data) {
set.add(str);
}
}
@Override
public boolean Interpret(String info) {
return set.contains(info);
}
}
package com.company.InterpreterPattern;
class NonterminalExprssion implements Expression {
private TerminalExpression region;
private TerminalExpression person;
public NonterminalExprssion(TerminalExpression region, TerminalExpression person) {
this.region = region;
this.person = person;
}
@Override
public boolean Interpret(String info) {
String[] str = info.split("的");
// B区的调试人员 --> str = {"B区", "调试人员"}
return region.Interpret(str[0]) && person.Interpret(str[1]);
}
}
package com.company.InterpreterPattern;
class Context {
private String[] regions = {"A区", "B区", "C区"};
private String[] persons = {"开发人员", "测试人员", "调试人员"};
private NonterminalExprssion nonterminal;
public Context() {
TerminalExpression region = new TerminalExpression(regions);
TerminalExpression person = new TerminalExpression(persons);
nonterminal = new NonterminalExprssion(region, person);
}
public void check(String info) {
boolean bool = nonterminal.Interpret(info);
if (bool) {
System.out.println("识别成功");
} else {
System.out.println("识别失败");
}
}
}
package com.company.InterpreterPattern;
public class InterpreterPattern {
public static void main(String[] args) {
Context context = new Context();
context.check("A区的开发人员");
context.check("B区的调试人员");
context.check("C区的测试人员");
System.out.println("==========");
context.check("D区的程序员");
context.check("D区的测试员");
context.check("A区的程序员");
}
}
适用性
Interpreter模式适用于当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时,以下情况效果最好:
1、该文法简单。对于复杂的发文,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无须构建抽象语法树即可解释表达式,这样可以节省空间还可能节省时间。
2、效率不是一个关键问题。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。不过,即使在这种情况下,转换器仍然可用该模式实现。
16、迭代器模式(Iterator)
提供一种方法顺序访问一个聚合集合对象中的各个元素,而不需要暴露该对象的内部表示。
package com.company.IteratorPattern;
interface Aggregate {
public Iterator CreateIterator();
}
package com.company.IteratorPattern;
interface Iterator {
public boolean hasNext();
public Object next();
}
package com.company.IteratorPattern;
class Book {
private String name;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
package com.company.IteratorPattern;
import java.util.ArrayList;
import java.util.List;
class BookAggregate implements Aggregate {
private List<Book> list = new ArrayList<Book>();
public void Add(Book book) {
list.add(book);
}
public Book get(int index) {
return list.get(index);
}
public int getSize() {
return list.size();
}
@Override
public Iterator CreateIterator() {
return new BookIterator(this);
}
}
package com.company.IteratorPattern;
class BookIterator implements Iterator {
private int index;
private BookAggregate bookAggregate;
public BookIterator(BookAggregate bookAggregate) {
this.index = 0;
this.bookAggregate = bookAggregate;
}
@Override
public boolean hasNext() {
if (index < bookAggregate.getSize()) {
return true;
} else {
return false;
}
}
@Override
public Object next() {
Object obj = bookAggregate.get(index);
index++;
return obj;
}
}
package com.company.IteratorPattern;
public class IteratorPattern {
public static void main(String[] args) {
BookAggregate bookAggregate = new BookAggregate();
String[] books = {"数据结构", "操作系统", "计算机网络", "计算机组成原理"};
double[] prices = {10.24, 20.48, 40.96, 81.92};
for (int i = 0; i < 4; i++) {
bookAggregate.Add(new Book(books[i], prices[i]));
}
Iterator bookIterator = bookAggregate.CreateIterator();
while (bookIterator.hasNext()) {
Book book = (Book) bookIterator.next();
System.out.println(book.getName() + " " + book.getPrice());
}
}
}
适用于
Iterator模式适用于:
1、访问一个聚合对象的内容而无须暴露它的内部展示。
2、支持对聚合对象的多种遍历。
3、为遍历不同的聚合结构提供一个统一的接口。
17、中介者模式(Mediator)
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散。而且可以独立地改变它们之间的交互。
package com.company.MediatorPattern;
abstract class Colleague {
protected Mediator mediator;
}
package com.company.MediatorPattern;
abstract class Mediator {
public abstract void sendMessage(String message, Colleague colleague);
}
package com.company.MediatorPattern;
class Colleague1 extends Colleague {
public Colleague1(Mediator mediator) {
this.mediator = mediator;
}
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
public void Notify(String message) {
System.out.println("同事1收到消息:" + message);
}
}
package com.company.MediatorPattern;
class Colleague2 extends Colleague {
public Colleague2(Mediator mediator) {
this.mediator = mediator;
}
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
public void Notify(String message) {
System.out.println("同事2收到消息:" + message);
}
}
package com.company.MediatorPattern;
class ConcreteMediator extends Mediator {
private Colleague1 colleague1;
private Colleague2 colleague2;
public void setColleague1(Colleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(Colleague2 colleague2) {
this.colleague2 = colleague2;
}
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.Notify(message); // 让同事2收到消息
} else {
colleague1.Notify(message); // 让同事1收到消息
}
}
}
package com.company.MediatorPattern;
public class MediatorPattern {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
Colleague1 colleague1 = new Colleague1(mediator);
Colleague2 colleague2 = new Colleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("软考加油");
colleague2.sendMessage("祝大家软考顺利通过!");
}
}
适用性
Mediator模式适用于:
1、一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
18、备忘录模式(Memento)
在不破坏封装性的前提下捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
package com.company.MementoPattern;
class Memento { // 备忘录
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
package com.company.MementoPattern;
class Originator { // 原发器
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void setMemento(Memento memento) {
state = memento.getState();
}
}
package com.company.MementoPattern;
import java.util.ArrayList;
import java.util.List;
class Caretaker { // 管理者
private List<Memento> mementoList = new ArrayList<>();
public void addMemento(Memento memento) {
mementoList.add(memento);
}
public Memento getMemento(int index) {
// 判断参数是否合法
if (index >= 1 && index <= mementoList.size()) {
return mementoList.get(index - 1);
}
return null;
}
public void showMemento() {
int cnt = 1;
// for (遍历对象类型 对象名 : 遍历对象)
for (Memento memento : mementoList) {
System.out.println("第" + cnt + "次备份,状态为:" + memento.getState());
cnt++;
}
}
}
package com.company.MementoPattern;
public class MementoPattern {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
originator.setState("1024");
Memento backup1 = originator.createMemento();
caretaker.addMemento(backup1);
originator.setState("2048");
Memento backup2 = originator.createMemento();
caretaker.addMemento(backup2);
originator.setState("4096");
Memento backup3 = originator.createMemento();
caretaker.addMemento(backup3);
System.out.println(originator.getState());
caretaker.showMemento();
Memento memento1 = caretaker.getMemento(2);
originator.setMemento(memento1);
System.out.println("根据第2次备份还原之后的状态为:" + originator.getState());
}
}
适用性
Memento模式适用于:
1、必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时他才能恢复到先前的状态。
2、如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
19、观察者模式(Observer)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
package com.company.ObserverPattern;
interface Subject { // 目标
public void Attach(Observer observer); // 添加观察者
public void Detach(Observer observer); // 删除观察者
public void Notify(); // 状态改变后 通知所有观察者
public void setState(String state); // 设置状态(改变状态)
public String getState(); // 获取状态
}
package com.company.ObserverPattern;
interface Observer { // 观察者接口
public void update(); // 收到通知 更新观察者的状态
}
package com.company.ObserverPattern;
import java.util.ArrayList;
import java.util.List;
class ConcreteSubject implements Subject {
private String name;
private String state;
private List<Observer> observerList;
public ConcreteSubject(String name) {
state = "未更新";
this.name = name;
observerList = new ArrayList<Observer>();
}
public void setState(String state) {
this.state = state;
System.out.println(name + "的状态发生变化,变化后的状态为:" + state);
Notify();
}
public String getState() {
return state;
}
public void Attach(Observer observer) {
observerList.add(observer);
}
public void Detach(Observer observer) {
observerList.remove(observer);
}
public void Notify() {
// for (遍历对象类型 对象名 : 遍历对象)
for (Observer observer : observerList) {
observer.update();
}
}
}
package com.company.ObserverPattern;
class ConcreteObserver implements Observer {
private String name;
private String state;
private Subject subject;
public ConcreteObserver(String name, Subject subject) {
this.name = name;
this.subject = subject;
subject.Attach(this);
state = subject.getState();
}
@Override
public void update() {
System.out.println(name + "收到通知");
state = subject.getState(); // 让当前观察者的状态 和 改变了状态之后的目标的状态保持一致
System.out.println(name + "改变后的状态为:" + state);
}
}
package com.company.ObserverPattern;
public class ObserverPattern {
public static void main(String[] args) {
Subject subjectA = new ConcreteSubject("目标A");
Observer observerB = new ConcreteObserver("张三", subjectA);
Observer observerC = new ConcreteObserver("李四", subjectA);
Observer observerD = new ConcreteObserver("王五", subjectA);
subjectA.setState("更新了");
System.out.println("======================================");
subjectA.Detach(observerD);
subjectA.setState("停更了");
}
}
适用性
Observer模式适用于:
1、当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使他们可以各自独立的改变和服用。
2、当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时。
3、当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是禁耦合的。
20、状态模式(State)
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
package com.company.StatePattern;
class Context { // 贩卖机
private int count;
private State state;
public Context() {
count = 3;
state = new StateA();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void Request() { // 购买一个饮料
state.Handle(this);
}
}
package com.company.StatePattern;
interface State {
public void Handle(Context context);
}
package com.company.StatePattern;
class StateA implements State { // 有货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count >= 1) {
System.out.println("购买成功!");
context.setCount(count - 1);
if (context.getCount() == 0) {
context.setState(new StateB());
}
} else {
System.out.println("购买失败!");
}
}
}
package com.company.StatePattern;
class StateB implements State { // 无货
@Override
public void Handle(Context context) {
int count = context.getCount();
if (count == 0) {
System.out.println("购买失败!等待补货");
context.setCount(5);
System.out.println("补货成功,请重新购买");
context.setState(new StateA());
}
}
}
package com.company.StatePattern;
public class StatePattern {
public static void main(String[] args) {
Context context = new Context(); // count:3
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 2
context.Request(); // 购买一个饮料 count = 1
context.Request(); // 购买一个饮料 count = 0
System.out.println(context.getState());
context.Request(); // 无货 等待补货 补货成功 count = 5
System.out.println(context.getState());
context.Request(); // 购买一个饮料 count = 4
System.out.println(context.getCount());
}
}
适用性
State模式适用于:
1、一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2、一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。
21、策略模式(Strategy)
在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
package com.company.StrategyPattern;
interface Strategy {
public void TwoNumberOperation(int a, int b);
}
package com.company.StrategyPattern;
public class OperationContext {
private Strategy strategy;
public OperationContext(Strategy strategy) {
this.strategy = strategy;
}
public void Operation(int a, int b) {
strategy.TwoNumberOperation(a, b);
}
}
package com.company.StrategyPattern;
class AddStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a + b);
}
}
package com.company.StrategyPattern;
class SubtractionStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a - b);
}
}
package com.company.StrategyPattern;
class MultiplyStrategy implements Strategy {
@Override
public void TwoNumberOperation(int a, int b) {
System.out.println(a * b);
}
}
package com.company.StrategyPattern;
public class StrategyPattern {
public static void main(String[] args) {
Strategy add = new AddStrategy();
Strategy subtraction = new SubtractionStrategy();
Strategy multiply = new MultiplyStrategy();
OperationContext context = new OperationContext(add);
context.Operation(2022, 528);
context = new OperationContext(subtraction);
context.Operation(2022, 528);
context = new OperationContext(multiply);
context.Operation(2022, 528);
}
}
应用实例
锦囊妙计:每个锦囊代表一个策略,包含不同的计策。
旅行方式选择:骑自行车、坐汽车等,每种方式都是一个可替换的策略。
Java AWT的LayoutManager:不同的布局管理器实现了相同的接口,但提供了不同的布局算法。
优点
算法切换自由:可以在运行时根据需要切换算法。
避免多重条件判断:消除了复杂的条件语句。
扩展性好:新增算法只需新增一个策略类,无需修改现有代码。
缺点
策略类数量增多:每增加一个算法,就需要增加一个策略类。
所有策略类都需要暴露:策略类需要对外公开,以便可以被选择和使用。
使用建议
当系统中有多种算法或行为,且它们之间可以相互替换时,使用策略模式。
当系统需要动态选择算法时,策略模式是一个合适的选择。
注意事项
如果系统中策略类数量过多,考虑使用其他模式或设计技巧来解决类膨胀问题。
结构
策略模式包含以下几个核心角色:
环境(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。
策略模式通过将算法与使用算法的代码解耦,提供了一种动态选择不同算法的方法。客户端代码不需要知道具体的算法细节,而是通过调用环境类来使用所选择的策略。
22、模版方法模式(Template Method)
定义一个操作中的算法骨架,而将一些步骤延迟到子类中,Template Method使得子类可以不改变一个算法结构即可重定义该算法的某些特定步骤。
package com.company.TemplateMethodPattern;
abstract class Person {
public void TemplateMethod() {
System.out.println("上课 去教室"); // 1
PrimitiveOperation1(); // 2
System.out.println("下课 离开教室"); // 3
PrimitiveOperation2(); // 4
}
public abstract void PrimitiveOperation1(); // 原语操作 1 :上课过程 学生 听课…… 老师 讲课
public abstract void PrimitiveOperation2(); // 原语操作 2 :作业 学生 写作业 提交作业…… 老师 批改作业 打分数
}
package com.company.TemplateMethodPattern;
class Student extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("学生:听课 学习 做笔记 提出问题");
}
@Override
public void PrimitiveOperation2() {
System.out.println("学生:写作业 提交作业");
}
}
package com.company.TemplateMethodPattern;
class Teacher extends Person {
@Override
public void PrimitiveOperation1() {
System.out.println("老师:上课 讲课 解答问题 布置作业");
}
@Override
public void PrimitiveOperation2() {
System.out.println("老师:批改作业 打分数");
}
}
package com.company.TemplateMethodPattern;
public class TemplateMethodPattern {
public static void main(String[] args) {
// 父类名 对象名 = new 子类名();
Person student = new Student();
Person teacher = new Teacher();
student.TemplateMethod();
System.out.println("=====我是分割线=====");
teacher.TemplateMethod();
}
}
适用性
Template Method模式适用于:
1、一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为应被提取出来并集中到一个公共的父类中,以避免代码重复。
3、控制子类扩展。模版方法旨在特定点调用“hook”操作(默认的行为,子类可以再必要时进行重定义扩展),这就只允许在这些点进行扩展。
23、访问者模式(Visitor)
表示一个作用于某对象结构中的各个元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。
package com.company.VisitorPattern;
abstract class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public abstract void Accept(Visitor visitor);
}
package com.company.VisitorPattern;
class Student extends Person {
private int score;
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public int getScore() {
return score;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitStudent(this);
}
}
package com.company.VisitorPattern;
class Teacher extends Person {
private int workYear;
public Teacher(String name, int age, int workYear) {
super(name, age);
this.workYear = workYear;
}
public int getWorkYear() {
return workYear;
}
@Override
public void Accept(Visitor visitor) {
visitor.visitTeacher(this);
}
}
package com.company.VisitorPattern;
import java.util.ArrayList;
import java.util.List;
class PersonStructure {
private List<Person> personList = new ArrayList<Person>();
public PersonStructure() {
personList.add(new Student("张三", 20, 70));
personList.add(new Student("李四", 21, 80));
personList.add(new Student("王五", 22, 90));
personList.add(new Teacher("李老师", 26, 3));
personList.add(new Teacher("陈老师", 27, 4));
personList.add(new Teacher("刘老师", 28, 5));
}
public void Accept(Visitor visitor) {
// for (遍历对象类型 对象名 : 遍历对象)
for (Person person : personList) {
person.Accept(visitor);
}
}
}
package com.company.VisitorPattern;
interface Visitor {
public void visitStudent(Student student); // 访问学生
public void visitTeacher(Teacher teacher); // 访问老师
}
package com.company.VisitorPattern;
class Visitor1 implements Visitor { // 访问者1 分别统计学生和老师的年龄总和
private int studentAgeSum = 0;
private int teacherAgeSum = 0;
public int getStudentAgeSum() {
return studentAgeSum;
}
public int getTeacherAgeSum() {
return teacherAgeSum;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者1访问学生:" + student.getName() + " 年龄:" + student.getAge());
studentAgeSum += student.getAge();
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者1访问老师:" + teacher.getName() + " 年龄:" + teacher.getAge());
teacherAgeSum += teacher.getAge();
}
}
package com.company.VisitorPattern;
class Visitor2 implements Visitor { // 访问者2 分别求出 学生的最高成绩 以及 老师的最高工龄
private int maxScore = -1;
private int maxWorkYear = -1;
public int getMaxScore() {
return maxScore;
}
public int getMaxWorkYear() {
return maxWorkYear;
}
@Override
public void visitStudent(Student student) {
System.out.println("访问者2访问学生:" + student.getName() + " 成绩:" + student.getScore());
maxScore = Math.max(maxScore, student.getScore());
}
@Override
public void visitTeacher(Teacher teacher) {
System.out.println("访问者2访问老师:" + teacher.getName() + " 工龄:" + teacher.getWorkYear());
maxWorkYear = Math.max(maxWorkYear, teacher.getWorkYear());
}
}
package com.company.VisitorPattern;
public class VisitorPattern {
public static void main(String[] args) {
PersonStructure structure = new PersonStructure();
Visitor1 visitor1 = new Visitor1();
System.out.println("访问者1的访问记录:");
structure.Accept(visitor1);
System.out.println("学生年龄的总和:" + visitor1.getStudentAgeSum() + " 老师年龄的总和:" + visitor1.getTeacherAgeSum());
System.out.println("=========================================");
Visitor2 visitor2 = new Visitor2();
System.out.println("访问者2的访问记录:");
structure.Accept(visitor2);
System.out.println("学生的最高成绩:" + visitor2.getMaxScore() + " 老师的最高工龄:" + visitor2.getMaxWorkYear());
}
}
适用性
Visitor模式适用于:
1、一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作“污染”这些对象的类。Visitor使得用户可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3、定义对象结构的类很少改变,但经常需要在次结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作比较好。