一、设计模式概述
1. 设计模式的重要性
- 软件工程中,设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。由埃里希·伽玛Erich Gamma 等人于1990年代从建筑设计领域引入到计算机科学中。
- 设计模式的目的有以下几个:
1.代码重用性(即:相同功能的代码,可以不用多次编写) 2.代码可读性(即:编程规范性,便于其他程序员的阅读和理解) 3.可拓展性(即:当需要增加新的功能时,非常方便做功能拓展) 4.可靠性(即:当我们增加新的功能后,对原来的功能没有影响) 5.使程序呈现高内聚,低耦合的特性
2. 设计模式需要遵循的六大原则
2.1 单一职责原则
- 定义
应该有且仅有一个原因引起类的变更(即:要求一个接口或者一个类只能有一个原因引起变化,也就是一个类或者一个接口只能有一个职责,它就负责一件事情。)
- 优点
类的复杂性降低,实现什么职责都有清晰明确的定义; 可读性高、可维护性; 变更引起的风险降低,变更时必不可少的,如果接口的单一职责做得好,一个接口修改只对响应的实现类有影响,对其他的接口无影响,这对系统的拓展性、维护性都有非常大的帮助; 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违背单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
2.2 接口隔离原则
- 定义
客户端不应该依赖它不需要的接口(即:一个类对另一个类的依赖应该建立在最小的接口上)。
2.3 依赖倒置原则
- 定义
程序要依赖于抽象接口,不要依赖于具体实现(即面向接口编程)。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。(在java中,抽象指的是抽象类或者接口,细节就是具体的实现类)
- 例子
/**
* 普通的实现
*/
public class Test{
public static void main(String[] args) {
Person person = new Person();
person.receive();
}
}
class Person {
//这里直接跟Email具体的实现类耦合,如果后面的需求要接收短信,微信怎么办?
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
class Email {
public String getInfo() {
return "收到电子邮件信息:周五凌晨5点,老地方交易!";
}
}
代码语言:java复制/**
* 采用依赖倒置原则(面向接口编程)
*/
public class Test{
public static void main(String[] args) {
Person person = new Person();
//客户端无需改动
person.receive(new Email());
person.recerve(new Sms());
}
}
class Person {
public void receive(Receiver receiver) {
System.out.println(receiver.getInfo());
}
}
//定义一个接收者接口
interface Receiver {
String getInfo()
}
Class Email implements Receiver {
public String getInfo() {
return "收到电子邮件信息:周五凌晨5点,老地方交易!";
}
}
Class Sms implements Receiver {
public String getInfo() {
return "收到短信:有内鬼,终止交易!";
}
}
2.4 里氏替换原则
- 定义
所有引用基类的地方必须能透明地使用其子类对象。
包含如下四层含义: 1、子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。(如果子类不能完整地继承父类的方法,或者父类的某些方法在子类中发生了重写或者重载,则建议断开父子继承关系,采用依赖、聚集、组合的关系代替继承) 2、子类可以增加自己的特性。 3、子类的方法重载父类方法时,方法的前置条件(形参)要比父类方法的输入参数更宽松。 4、覆写或者实现父类的方法时,输出结果(返回值)可以被缩小。
2.5 开闭原则
- 定义
一个软件实体(如类、模块、函数)应该对拓展开放(提供方),对修改关闭(使用方)。用抽象构建架构,用实现拓展细节。当软件需要变化时,尽量通过拓展软件实体的行为来实现变化,而不是通过修改已有代码来实现变化。
- 例子
/**
* 普通的实现
*/
public class Test{
public static void main(String[] args) {
GraphEditor editor = new GraphEditor();
editor.drawShape(ShareTypeEnum.RECTANGLE);
}
}
class GraphEditor {
public drawShape(ShareTypeEnum type) {
if(ShareTypeEnum.RECTANGLE.equal(type)) {
system.out.println("绘制矩形");
} else if (ShareTypeEnum.CIRCLE.equals(type)) {
system.out.println("绘制圆形");
}
}
}
enum ShareTypeEnum {
RECTANGLE,
CIRCLE;
}
代码语言:java复制/**
* 开闭原则
*/
public class Test {
public static void main(String[] args) {
GraphEditor editor = new GraphEditor();
editor.drawShape(new Rectangle());
editor.drawShape(new Circle());
}
}
interface Shape {
void draw();
}
class GraphEditor {
public void drawShape(Shape shape) {
shape.draw();
}
}
class Rectangle implements Shape {
public void draw() {
System.out.println("绘制矩形");
}
}
class Circle implements Shape {
public void draw() {
System.out.println("绘制圆形");
}
}
2.6 迪米特原则
- 定义
迪米特法则(LoD)也叫最少知道法则:一个对象应该对其他对象有最少的了解。
二、创建型设计模式
1. 单例模式
- 定义
指采取一定的手段保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得该对象实例的方法。一般分为饿汉式、懒汉式、双重检查、静态内部类、枚举等。
- JDK中的应用
java.lang.Runtime就是单例模式。
- 使用场景
1、需要频繁的进行创建和销毁的对象,且创建对象时消耗过多或耗费资源或多,但又经常要用到的对象。
1.1 饿汉式
代码语言:java复制/**
* 饿汉式
* 优点:写法简单,在类装载时就完成实例化,避免了同步问题;
* 缺点:容易造成内存浪费
*/
class Singleton {
//2.本类内部创建对象实例
private static final Singleton INSTANCE = new Singleton();
//1.私有化构造器
private Singleton {}
//3. 提供一个公有的静态方法,返回实例
public static getInstance() {
return INSTANCE;
}
}
1.2 懒汉式
代码语言:java复制/**
* 懒汉式(同步方法实现)
* 优点:写法简单,按需加载,线程安全;
* 缺点:效率低(锁粒度大,每次执行getInstance方法都要加锁)
*/
class Singleton1 {
private static Singleton1 instance;
private Singleton1() {}
//同步方法,解决线程不安全问题
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton1();
}
return instance;
}
}
1.3 双重检查
代码语言:java复制/**
* 双重检查
*/
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
//第一重检查
if (instance == null) {
synchronized(Singleton.class) {
//第二重检查
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
1.4 静态内部类
代码语言:java复制/**
* 静态内部类(利用静态内部类的特点和类加载机制保证只有一个线程)
* - Singleton类被装载时,不会立即实例化,当首次调用getInstance方法时,会装载SingletonInsatnce类,
* 从而完成Singleton类的实例化
* - 类的静态属性只会在第一次加载类的时候初始化,所以在这里JVM帮助我们保证了线程的安全。
*/
class Singleton {
//1.私有化构造方法
private Singleton() {}
//3.提供一个公有的静态方法,返回实例
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
//2.静态内部类实例化Singleton类
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
}
1.5 枚举
代码语言:java复制enum Singleton {
INSTANCE;
public void doSomething() {
}
}
2. 原型模式
- 定义
指用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
- 实现方式
/**
* 浅拷贝(使用clone()方法实现)
* a)成员变量为基本数据类型时,会直接进行值传递(即将成员变量的值复制一份给新对象);
* b)成员变量为引用数据类型时,只进行引用传递(即将成员变量的引用复制一份给新对象)。
*/
class Prototype implements Cloneable {
private String prop;
public Prototype(String prop) {
super();
this.prop = prop;
}
}
public class Cleint{
public static void main(String[] args) {
Prototype p = new Prototype("x");
Prototype p1 = p.clone();
}
}
代码语言:java复制/**
* 深拷贝(通过重写clone()或者对象序列化实现)
*/
@Data
class DeepPrototype implements Serializable,Cloneable {
private String prop;
private MObject m;
public DeepPrototype () {
super();
}
//方式一、重写clone方法
@Override
protected Object clone() throw CloneNotSupportedException {
DeepPrototype deep = null;
//完成基本数据类型克隆
deep = (DeepPrototype)super.clone();
//对引用数据类型进行克隆
deep.setM((MObject)m.clone());
return deep;
}
//方式二
public Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (DeepPrototype) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3. 工厂方法模式
- 定义
一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类中。
- 结构
1、抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
2、具体工厂:主要时实现抽象工厂中的抽奖方法,完成具体产品的创建。
3、抽象产品:定义了产品的规范,描述了产品的主要性能和功能。
4、具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
- 例子
interface Phone {
void call();
}
class XiaoMiPhone implements Phone {
public void call() {
System.out.println("小米手机打电话");
}
}
class HuaweiPhone implements Phone {
public void call() {
System.out.println("华为手机打电话");
}
}
abstract class PhoneFactory {
public abstract Phone make();
}
class XiaomiPhoneFactory extends PhoneFactory {
public Phone make() {
return new XiaoMiPhone();
}
}
class HuaweiPhoneFactory extends PhoneFactory {
public Phone make() {
return new HuaweiPhone();
}
}
class Client {
public static void main(String[] args) {
PhoneFactory factory = new HuaweiPhoneFactory();
factory.make().call();
}
}
- 特点
优点:
1、用户只需要知道具体工厂的名称就可以得到所想要的产品,无需知道产品的具体创建过程;
2、在系统新增新的产品时哦,只需要添加具体的产品类和工厂类,无需对原工厂进行任何修改,满足开闭原则;
缺点:
每增加一个产品,都需要增加一个具体的产品类和具体工厂类,增加了系统的复杂度。
4. 抽象工厂模式
- 定义
定义一个接口用于创建相关或有依赖关系的对象簇,而不需指明具体的类。
- 例子
5. 创建者模式(Builder)
略。(参考lombok的Builder注解)
三、结构型设计模式
1. 代理(Proxy)模式
- 定义
为一个对象提供一个替身,以控制对这个对象的访问(即通过代理对象访问目标对象)。
- 结构
类型:
1、静态代理
2、动态代理:包括JDK代理、接口代理、Cglib代理(可以在内存动态的创建对象,不需要实现接口)
- 例子
/**
* 静态代理
*/
interface Image {
void display();
}
class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadingFromDisk(fileName);
}
public void display() {
System.out.println("display " fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("loading " fileName);
}
}
class ImageProxy implements Image {
private RealImage realImage;
private String fileName;
public ImageProxy(String fileName) {
this.fileName = fileName;
}
public void display() {
if(realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
class Client {
//静态代理
public void admire() {
Image image = new ImageProxy("葵花宝典.png");
image.display();
//看第二次
image.display();
}
//JDK代理(不需要写ImageProxy代理类)
public void admire2() {
//创建目标对象
Image image = new RealImage("降龙十八掌.png");
//给目标对象创建代理对象
Image proxyImage = (Image)new ProxyFacotry(image).getProxyInstance();
//通过代理对象执行目标对象的方法
proxyImage.display();
}
//Cglib代理(不需要写Image接口)
public void admire3() {
RealImage image = new RealImage("打狗棒法.png");
RealImage proxyImage = (RealImage) new CglibProxyFactory(image).getProxyInstance();
proxyImage.display();
}
}
/**
* jdk代理(解决静态代理需要创建大量的Proxy类)
*/
class ProxyFactory {
//维护一个目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成一个代理对象
public Object getProxyInstance() {
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
System.out.println("do something before execute");
Object result =method.invoke(target, args);
System.out.println("do something after execute");
return result;
}
});
}
}
/**
* Cglib代理(解决Jdk代理需要求目标对象必须实现接口问题)
* 原理:
* Cglib也叫子类代理,它是在内存中构建一个字类对象从而实现对目标对象功能的推展,
* 其底层是通过字节码处理框架ASM来转换字节码并生成新的类。
* 引入包
* <dependency>
* <groupId>cglib</groupId>
* <artifactId>cglib</artifactId>
* <version>3.2.5</version>
* </dependency>
*/
class CglibProxyFactory implements MethodInterceptor {
//维护一个目标对象
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
//返回一个代理对象
public Object getProxyInstance() {
//创建于个工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass);
//设置回调方法
enhancer.setCallback(this);
//创建子类对象(即代理对象)
return enhancer.create();
}
//重写intercept方法,会调用目标对象的方法
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("do something before execute");
Object result = method.invoke(target, args);
System.out.println("do something after execute");
return result;
}
}
- 特点
优点:
采用代理模式,可以在目标对象的实现基础上,增强额外的功能操作(即拓展了目标对象的功能)。
- 应用
AOP
2. 适配器(Adapter)模式
- 定义
将一个接口转换为客户端所期待的接口,从而使两个接口不兼容的类可以在一起工作。
- 结构
1、target:最终需要的输出;
2、Adapter:适配器;
3、source:需要被适配的类、接口、对象。
- 例子
//原有类 输出220v
class AC220V {
public int output220V() {
return 220;
}
}
//目标接口 需要输入5V
interface DC5V {
int output5V();
}
//方式一:类适配(继承source类,实现target接口)
class ChargerAdapter extends AC220V implements DC5V {
public int output5V () {
int output = output220V();
return output / 44;
}
}
//方式二:对象适配(通过关联关系-聚合)
@AllArgsConstructor
class ChargerAdapter2 implements DC5V {
private final AC220V ac220v;
public int output5V() {
return ac220v.output220v() / 44;
}
}
//接口适配:当不需要全部实现接口提供的方法时,
//可以设计一个抽象类实现接口,并为该接口中的每一个方法提供一个默认的实现(空方法),
//那么该抽象类的子类就可以有选择地覆盖父类的某些方法来实现需求
- 应用
SpringMVC框架的HandlerAdapter。
3. 桥接(Bridge)模式
- 定义
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- 结构
1、抽象类;
2、拓展抽象类;
3、实现类接口;
4、具体实现类。
- 例子
/**
* T恤有印花,颜色,尺码三种属性
*/
interface Size {
String getSize();
}
class S implements Size {
public String getSize() {
return "S";
}
}
class L implements Size {
public String getSize() {
return "L";
}
}
interface Color {
String getColor();
}
class Red implements Color() {
public String getColor() {
return "红色";
}
}
@AllArgsConstructor
abstract class TShirt {
//聚合属性
private Color color;
private Size size;
protected abstract String getPrints();
protected String getParam() {
return "颜色:" color.getColor() ",尺码:" size.getSize() ",印花:" getPrints();
}
}
class PureTShirt extends TShirt {
public PureTShirt (Color color, Size size) {
super(color, size);
}
protected String getPrints() {
return "无";
}
}
class Client {
public void buyTShirt() {
TShirt tShirt = new PureTShirt(new Red(), new L());
System.out.println(tShirt.getParam());
}
}
- 特点
1、提高了系统的可拓展性,不需要修改原有的系统情况下进行任意维度的拓展。
2、增加了系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就要针对抽象层进行设计与编程。
4. 装饰器(Decorator)模式
- 定义
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。在对象功能拓展方面,它比继承更有弹性。
- 结构
1、抽象构建角色:给出一个抽象接口,以规范准备接收附加责任的对象;
2、具体构建角色:实现抽象构建,并通过装饰角色为其添加一些职责;
3、抽象装饰角色:继承抽象构建角色,并包含具体构建的实例,可以通过其子类拓展具体构建的功能;
4、具体装饰角色:实现抽象装饰的相关方法,并给具体构建对象添加附加的责任。
- 例子
//抽象构建角色
@Data
abstract class Drink {
private String desc;
private float price;
public abstract float cost();
}
//具体构建角色
class Coffee extends Drink {
public float cost() {
return super.getPrice();
}
}
class Mocha extends Coffee {
public Mocha() {
setDesc("摩卡");
setPrice(10.0f);
}
}
class Latte extends Coffee {
public Latte() {
setDesc("拿铁");
setPrice(12.0f);
}
}
//抽象装饰角色
abstract class Decorator extends Drink {
//组合
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
//重写cost、getDesc方法
public float cost() {
return drink.getPrice() super.getPrice();
}
public String getDesc() {
return super.getDesc() "、" drink.getDesc();
}
}
//具体的实现类
class Milk extends Decorator {
public Milk (Drink drink) {
super(drink);
setPrice(8.0f);
setDesc("牛奶");
}
}
class Ice extends Decorator {
public Ice(Drink drink) {
super(drink);
setDesc("冰块");
setPrice(2.0f);
}
}
class Client {
public void bugCoffee() {
Drink coffee = new Mocha();
coffee = new Ice(coffee);
coffee = new Milk(coffee);
System.out.println("配料:" coffee.getDesc() ",花费:" coffee.getPrice());
}
}
- 特点
1、装饰器模式是对继承的有利补充,比继承灵活,在不改变原有对象的情况下,动态地拓展功能,即插即用;
2、同步哦使用不同的装饰类及这些装饰类的排列组合,可以实现不同的效果。
3、当现有类需要同台添加或撤销附加职责时,或者现有的一组基本功能进行排列组合而产生非常多的功能时可以考虑使用装饰器模式。
- 应用
IO操作的Buffer类。
5. 外观(Facade)模式
略(可以参考微服务的网关)
6. 享元(Flyweight)模式
- 定义
如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。(目的是提高系统性能)
- 结构
角色:
1、享元工厂:用于创建具体享元的享元对象,维护相同的享元对象;
2、抽象享元:定义需要共享的对象业务接口;
3、具体享元:实现抽象享元的接口,完成某一具体逻辑。
状态:
1、内部状态:不随环境的变化而变化,在各个对象间共享;
2、外部状态:随环境的改变而改变,由客户端传入。
- 例子
/**
* 棋子分黑、白两种颜色(内部状态),棋子落子位置每个都不一样(外部状态)
*/
//定义棋子抽象类
abstract class Chess {
abstract String getChessType();
protected void draw(int x, int y) {
System.out.println(String.format("%s棋子落在(%d,%d)处", getChessType(), x, y));
}
}
//棋子的具体实现
class BlackChess extends Chess{
public String getChessType() {
return "黑";
}
}
class WhiteChess extends Chess{
public String getChessType() {
return "白";
}
}
//棋子对象工厂
public class ChessFactory {
private static final Map<String, Chess> map = new HashMap();
private static final ChessFactory INSTANCE = new ChessFactory();
private ChessFactory() {}
public static ChessFactory getInstance() {
return INSTANCE;
}
public Chess getChess(String type) {
Chess chess = map.get(type);
if (chess != null) {
return chess;
}
chess = "黑".equals(type) ? new BlackChess() : new WhiteChess();
map.put(type, chess);
return chess;
}
}
class client {
public void play() {
ChessFactory factory = ChessFactory.getInstance();
Chess c1 = factory.getChess("白");
c1.draw(1,2);
}
}
- 特点
优点:
可以极大地减少系统种对象的创建,减少内存空间,提高效率。
缺点:
对对象需要明确区分内部状态和外部状态,使系统逻辑变复杂。
- 应用
数据库的连接池、String常量池、缓冲池等。
7. 组合(Composite)模式
- 定义
对象组合成树形结构以表示“部分-整体”的层次结构,用户对单个对象和组合对象的使用具有一致性。
- 结构
- 例子
@AllArgsConstructor
@Getter
abstract class OrgComponent {
private name;
public abstract void add(OrgComponent orgComponent);
public abstract OrgComponent getChild(String name);
public abstract int getStaffCount();
}
//组合类
class OrgComposite extends OrgComponent {
private List<OrgComponent> orgList;
public OrgComposite(String name) {
super(name);
orgList = new ArrayList();
}
public void add(OrgComponent org) {
orgList.add(org);
}
public OrgComponent getChild(String name) {
for(OrgComponent org : orgList) {
OrgComponent child = org.getChild(name);
if(child != null) {
return child;
}
}
return null;
}
public int getStaffCount() {
int count = 0;
for(OrgComponent org : orgList) {
count = org.getStaffCount();
}
return count;
}
}
//叶子结点
class ItDepartment extends OrgComonent {
public ItDepartment(String name) {
super(name);
}
public void add(OrgComponent org) {
throw new UnsupportedOperationException(this.getName() "已经是基本部");
}
public OrgComponent getChild(String name) {
if(getName.equals(name)) {
return this;
}
return null;
}
public int getStaffCount() {
return 20;
}
}
class AdminDepartment extends OrgComonent {
public AdminDepartment(String name) {
super(name);
}
public void add(OrgComponent org) {
throw new UnsupportedOperationException(this.getName() "已经是基本部");
}
public OrgComponent getChild(String name) {
if(getName.equals(name)) {
return this;
}
return null;
}
public int getStaffCount() {
return 20;
}
}
class Client {
public void constructOrg() {
OrgComposite group = new OrgComposite("碧桂园集团");
OrgComposite head = new OrgComposite("集团总部");
OrgComposite tsb = new OrgComposite("提升办");
OrgComposite sg = new OrgComposite("数管");
ItDepartment ptb = new ItDepartment("数字化平台部");
group.add(head);
head.add(tsb);
tsb.add(sg);
sg.add(ptb);
}
}
- 特点
优点:高层模块调用简单(局部和整体对于调用者来说没有任何区别,不必关心自己处理的是单个对象还是整个组合结构);
缺点:其叶子和树枝节点的生命都是实现类,违反依赖倒置原则。
- 应用
swing中的容器和组件(Container等容器是树枝,Button等组件是叶子)。
四、行为型设计模式
1. 模板方法(Template Method)模式
- 定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
- 结构
1、抽象模板:实现了模板方法,定义了算法的骨架。
2、具体模板:实现抽象类中的抽象方法,已完成完整的算法。
- 例子
abstract class Practitioner {
public final void study() {
System.out.println("个人练习生学习开始……");
skill1();
skill2();
skill3();
skill4();
System.out.println("学成!");
}
abstract void skill1();
abstract void skill2();
abstract void skill3();
abstract void skill4();
}
class CaiXukun extends Practitioner {
public void skill1() {
System.out.println("6个月后完成技能一:唱");
}
public void skill2() {
System.out.println("8个月后完成技能二:跳");
}
public void skill3() {
System.out.println("8个月后完成技能一:Rap");
}
public void skill1() {
System.out.println("8个月后完成技能一:篮球");
}
}
class Client {
public void test() {
Practitioner practitioner1 = new CaiXukun();
practitioner1.study();
}
}
- 特点
1、封装不变部分,扩展可变部分。把认为不变部分的算法封装到父类中实现,而可变部分的则可以通过继承来继续扩展。可以将代码最大复用化。
2、父类规范算法行为,子类提供完整实现。
2. 策略(Strategy)模式
- 定义
定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。
- 结构
1、抽象策略:对策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性;
2、具体策略:用于实现抽象策略中的操作,即实现具体的算法;
3、上下文:起承上启下的作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
- 例子
//策略接口
interface PaymentStrategy {
void payment(BigDecimal amount);
}
//策略实现类
class CreditPaymentStrategy implements PaymentStrategy {
public void payment(BigDecimal amount) {
System.out.println("银行卡支付" amount.toPlainString() "元");
}
}
class WechatPaymentStrategy implements PaymentStrategy {
public void payment(BigDecimal amount) {
System.out.println("微信支付" amount.toPlainString() "元");
}
}
class ZhifubaoPaymentStrategy implements PaymentStrategy {
public void payment(BigDecimal amount) {
System.out.println("支付宝支付" amount.toPlainString() "元");
}
}
//上下文
class PaymentService {
public void payment(PaymentStrategy strategy, BigDecimal amount) {
strategy.payment(amount);
}
}
class Client {
public static void main(String[] args) {
PaymentService service = new PaymentService();
service.payment(new WechatPaymentStrategy(), new Bigcimal(10.00));
}
}
- 特点
优点
1、算法可以自由切换;
2、避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护);
3、扩展性良好,增加一个策略只需实现接口即可。
缺点
4、策略类数量会增多,每个策略都是一个类,复用的可能性很小
5、所有的策略类都需要对外暴露
- 应用
3. 命令(Command)模式
- 定义
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 结构
1、抽象命令:声明执行命令的接口,拥有执行命令的抽象方法。
2、具体命令:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
3、调用者(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,不直接访问接收者。
4、接收者(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 例子
//命令接收者
class TV {
public void on() {
System.out.println("打开电视");
}
public void off() {
System.out.println("关闭电视");
}
}
//抽象的命令
abstract class TVCommand {
//聚合执行者
private TV tv;
public TVCommand(TV tv) {
this.tv = tv;
}
public TV getTv() {
return this.tv;
}
public abstract void execute();
}
//具体的命令
class OnTVCommand extends TVCommand {
public OnTVCommand(TV tv) {
super(tv);
}
public void execute() {
System.out.println("电视打开中……");
getTv().on();
}
}
class OffTVCommand extends TVCommand {
public OffTVCommand(TV tv) {
super(tv);
}
public void execute() {
System.out.println("电视关闭中……");
getTv().off();
}
}
//命令发起者
class RemoteController {
//聚合电视命令
private TVCommand onCommand;
private TVCommand offCommand;
public RemoteController(OnTVCommand on, OffTVCommand off) {
this.onCommand = on;
this.offCommand = off;
}
public void on() {
onCommand.execute();
}
public void off() {
offCommand.execute();
}
}
class Client {
public void client () {
TV tv = new TV();
RemoteController controller = new RemoteController(new OnTVCommand(tv), new OffTVCommand(tv));
controller.on();
controller.off();
}
}
- 特点
1、降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
2、增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
3、可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
4. 责任链(Chain of Responsibility)模式
- 定义
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止
- 结构
1、抽象处理者(Handler):定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。Handler 类的聚合关系给出了具体子类对下家的引用,抽象方法 规范了子类处理请求的操作。
2、具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。
- 例子
class Request {
private String name;
private BigDecimal amount;
public Request(String name, BigDecimal amount) {
this.name = name;
this.amount = amount;
}
public String getName() {
return name;
}
public BigDecimal getAmount() {
return amount;
}
}
interface Handler {
// 返回Boolean.TRUE = 成功
// 返回Boolean.FALSE = 拒绝
// 返回null = 交下一个处理
Boolean process(Request request);
}
class ManagerHandler implements Handler {
public Boolean process(Request request) {
// 如果超过1000元,处理不了,交下一个处理:
if (request.getAmount().compareTo(BigDecimal.valueOf(1000)) > 0) {
return null;
}
return new Random().nextBoolean();
}
}
class DirectorHandler implements Handler {
public Boolean process(Request request) {
// 如果超过10000元,处理不了,交下一个处理:
if (request.getAmount().compareTo(BigDecimal.valueOf(10000)) > 0) {
return null;
}
return new Random().nextBoolean();
}
}
class CEOHandler implements Handler {
public Boolean process(Request request) {
return new Random().nextBoolean();
}
}
class HandlerChain {
// 持有所有Handler:
private List<Handler> handlers = new ArrayList<>();
public void addHandler(Handler handler) {
this.handlers.add(handler);
}
public boolean process(Request request) {
// 依次调用每个Handler:
for (Handler handler : handlers) {
Boolean r = handler.process(request);
if (r != null) {
// 如果返回TRUE或FALSE,处理结束:
System.out.println(request.getName() " " (r ? "Approved by " : "Denied by ") handler.getClass().getSimpleName());
return r;
}
}
throw new RuntimeException("Could not handle request: " request);
}
}
class Client {
public static void main(String[] args) {
// 构造责任链:
HandlerChain chain = new HandlerChain();
chain.addHandler(new ManagerHandler());
chain.addHandler(new DirectorHandler());
chain.addHandler(new CEOHandler());
// 处理请求:
chain.process(new Request("Bob", new BigDecimal("123.45")));
chain.process(new Request("Alice", new BigDecimal("1234.56")));
chain.process(new Request("Bill", new BigDecimal("12345.67")));
chain.process(new Request("John", new BigDecimal("123456.78")));
}
}
- 特点
优点:
1、降低耦合度。它将请求的发送者和接收者解耦。
2、简化了对象。使得对象不需要知道链的结构。
3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
4、增加新的请求处理类很方便。
缺点:
1、不能保证请求一定被接收。
2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
3、可能不容易观察运行时的特征,有碍于除错。
5. 状态(State)模式
- 定义
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
- 结构
1、上下文角色(Context):上下文角色一般是一个类,上下文角色会聚合很多和 state,这些 state 使用静态常量修饰,并且负责 state 的状态切换;另外上下文角色还会包含抽象状态角色中定义的所有行为如 request,然后内部将请求委托给 state 的 handle 处理;
2、抽象状态角色(State):抽象状态角色一般是一个抽象类,用来定义具体状态的公共行为比如 handle,任何具体状态都必须实现该抽象类中的抽象方法;
3、具体状态角色(ConcreteState):继承抽象状态角色,实现抽象方法,实际处理来自 Context 的委托请求,当 Context 改变状态时行为也跟着改变。
- 例子
class TV {
//聚合电视状态
public static final TVState POWER_OFF_STATE = new PowerOffState();
public static final TVState STANDBY_STATE = new StandbyState() ;
public static final TVState PLAY_STATE = new PlayState();
// 标识当前状态
private TVState currentState;
public TV() {
setCurrentState(POWER_OFF_STATE);
}
public TVState getCurrentState() {
return currentState;
}
// 设置当前状态,遥控器负责电视机的具体状态切换
public void setCurrentState(TVState currentState) {
this.currentState = currentState;
this.currentState.setTv(this);
}
//委托给state统一去处理
public void powerOn() {
this.currentState.powerOn();
}
public void powerOff() {
this.currentState.powerOff();
}
public void play() {
this.currentState.play();
}
public void standby() {
this.currentState.standby();
}
}
// 抽象的电视机状态角色
abstract class TVState {
// 使用遥控器作为上下文,控制电视机状态的切换
protected TV tv;
public TV getTv() {
return tv;
}
public void setTv(TV tv) {
this.tv = tv;
}
// 开机
abstract void powerOn();
// 关机
abstract void powerOff();
// 播放
abstract void play();
// 待机
abstract void standby();
}
class PowerOffState extends TVState {
@Override
void powerOn() {
System.out.println("开机。。。");
//变更状态
tv.setCurrentState(TV.STANDBY_STATE);
//执行待机行为
tv.getCurrentState().standby();
}
@Override
void powerOff() {
//do nothing
}
@Override
void play() {
//do nothing
}
@Override
void standby() {
//do nothing
}
}
class StandbyState extends TVState {
@Override
void powerOn() {
//do nothing
}
@Override
void powerOff() {
System.out.println("关机。。。");
tv.setCurrentState(TV.POWER_OFF_STATE);
tv.getCurrentState().powerOff();
}
@Override
void play() {
System.out.println("播放。。。");
tv.setCurrentState(TV.PLAY_STATE);
tv.getCurrentState().play();
}
@Override
void standby() {
//do nothing
}
}
class PlayState extends TVState {
@Override
void powerOn() {
// do nothing
}
@Override
void powerOff() {
System.out.println("关机。。。");
tv.setCurrentState(TV.POWER_OFF_STATE);
tv.getCurrentState().powerOff();
}
@Override
void play() {
//do nothing
}
@Override
void standby() {
System.out.println("待机。。。");
tv.setCurrentState(TV.STANDBY_STATE);
tv.getCurrentState().standby();
}
}
class Client {
public static void main(String[] args) {
TV tv = new TV();
tv.powerOff();
tv.powerOn();
tv.play();
tv.standby();
tv.powerOff();
}
}
- 特点
1、减少代码体积,利于拓展:状态模式可以消除繁杂的条件判断语句块,使得业务逻辑清晰,很好地应对对象状态的增加、删除的业务场景,因为添加新的状态只需要增加新的状态类就好了;
2、状态模式状态很多时会导致状态类比较多,子类太多的时候就不方便维护管理了。
- 应用
6. 观察者(Observer)模式
- 定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 结构
1、抽象主题(Subject): 定义了被观察者常用的方法,订阅(attach)、取消订阅(detach)和通知(notify)功能;
2、具体主题(ConcreteSubject):实现抽象主题定义的方法,通过attach和detach方法维护一个观察者的集合,当自己维护的状态(state)改变时通知(notify)所有观察者;
3、抽象观察者(Observer):定义更新自己的方法;
4、具体观察者(ConcreteObserver):实现更新自己的的方法。
- 例子
//抽象主题
interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyAllObservers();
}
//抽象观察者
abstract class Observer {
protected Subject subject;
public Observer(Subject subject) {
this.subject = subject;
subject.attach(this);
}
public abstract void update();
}
//具体的主题
class DataSubject implements Subject {
private List<Observer> observerList = new ArrayList();
private int data;
public void attach(Observer observer) {
observerList.add(observer);
}
public void detach(Observer observer) {
observerList.remove(observer);
}
public void notifyAllObservers() {
observerList.forEach(o -> o.update())
}
public void setData(int data) {
this.data = data;
notifyAllObservers();
}
public int getData() {
return data;
}
}
//具体观察者
class BinObserver extends Observer {
public BinaryObserver(Subject subject) {
super(subject);
}
public void update() {
System.out.println(String.format("Binary String:%s", Integer.toBinaryString(subject.getState())));
}
}
class OctObserver extends Observer {
public OctObserver(Subject subject) {
super(subject);
}
public void update() {
System.out.println(String.format("Octal String: %s", Integer.toOctalString(subject.getState())));
}
}
class HexObserver extends Observer {
public HexObserver(Subject subject) {
super(subject)
}
public void update() {
System.out.println(String.format("Hex String: %s", Integer.toHexString(subject.getState())));
}
}
class Client {
public static void main(String[] args) {
Subject subject = new DataSubject();
new BinObserver(subject);
new OctObserver(subject);
new HexObserver(subject);
subject.setData(4);
subject.setData(8);
}
}
- 特点
优点
1、满足了当一个对象的改变需要改变其他对象这个条件的前提下,实现了松耦合。
2、符合开闭原则,继承抽象主题添加被观察者,继承抽象观察者添加观察者。
缺点
1、如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 应用
在java类库中有java.util.Observer和java.util.Observable作为观察者和被观察者
7. 中介者(Mediator)模式
- 定义
用一个中介对象来封装一系列的对象交互。
- 结构
1、抽象的中介者(Mediator):定义了同事对象到中介者对象的接口;
2、具体的中介者(ConcreteMediator):需要知道所有的具体的同事类,并接受某个同事对象的消息并完成相应的任务;
3、抽象的同事(Colleague):定义了中介者对象接口,它只知道中介者而不知道其他同事对象。
4、具体的同事(ConcreteColleague):每个同事只知道自己的行为,而不了解其他同事类的行为,但它们都依赖中介者。
- 例子
//抽象中介者
abstract class ChatMediator {
public abstract void register(ChatClient client);
public abstract void notice(ChatClient client,String message);
}
//抽象同事
abstract class ChatClient {
//聚合中介者
protected ChatMediator mediator;
public ChatClient(ChatMediator mediator){
this.mediator = mediator;
}
public abstract void sendMessage(String message);
public abstract void receiveMessage(String message);
}
//具体的中介者
class ChatServer extends ChatMediator {
private List<ChatClient> clientList = new ArrayList<>();
public void notice(ChatClient client, String message) {
for (ChatClient c : clientList) {
if (!client.equals(c)) {
c.receiveMessage(message);
}
}
}
public void register(ChatClient client) {
if (client != null && !clientList.contains(client)) {
clientList.add(client);
}
}
}
//具体的同事类
class Partner extends ChatClient {
private String name;
public Partner(ChatMediator mediator, String name) {
super(mediator);
this.name = name;
mediator.register(this);
}
@Override
public void sendMessage(String message) {
System.out.println(name "发送一条消息:" message);
mediator.notice(this, message);
}
@Override
public void receiveMessage(String message) {
System.out.println(name "收到一条消息:" message);
}
}
class Client {
public static void main(String[] args) {
//聊天服务器
ChatServer chatServer = new ChatServer();
//三个小伙伴
Partner andy = new Partner(chatServer, "Andy");
Partner ben = new Partner(chatServer, "Ben");
Partner cat = new Partner(chatServer, "Cat");
System.out.println("=====群聊信息=====");
andy.sendMessage("今晚去看复仇者联盟4吧!");
System.out.println("-----——分割线——-----");
ben.sendMessage("好啊好啊~我期待了很久!");
System.out.println("-----——分割线---——--");
cat.sendMessage("我来负责买票!!!");
}
}
- 特点
优点:
1、利用中介者模式可以将多个相互耦合的类进行解耦(类似于将网状结构分离成星型结构);
2、减少类间的依赖,降低了耦合,符合迪米特法则;
缺点:
1、中介者承担了较多的责任,一旦出现问题,整个系统就会受到影响;
2、如果设计不当,中介者对像本身会变得过于复杂。
8. 迭代器(Iterator)模式
- 定义
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
- 结构
1、抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
2、具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
3、抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
4、具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
- 特点
- 应用
9. 访问者(Visitor)模式
- 定义
访问者模式是一种将数据操作和数据结构分离的设计模式。它可以在不改变数据结构的前提下定义作用于这些元素的新操作。其目的是将数据结构和数据操作分离,解决数据结构和操作耦合性问题。
- 结构
1、抽象访问者(Visitor):定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
2、具体访问者(ConcreteVisitor):它需要给出对每一个元素类访问时所产生的具体行为。
3、元素接口(Element):它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
4、具体的元素(Element):它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
5、结构对象(ObjectStructure):对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
- 例子
/**
* 访问者模式
* 原理:在被访问的类里面加一个对外提供待访问者的接口。
*/
//抽象的元素
abstract class Staff {
private String name;
private int kpi;
public Staff(String name, int kpi) {
this.name = name;
this.kpi = kpi;
}
public String getName() {
return name;
}
public int getKpi() {
return kpi;
}
//接受Visitor的访问
public abstract void accept(Visitor visitor);
}
//具体的元素
class Engineer extends Staff {
private int codeLine;
public Engineer(String name, int kpi, int codeLine) {
super(name, kpi);
this.codeLine = codeLine;
}
public int getCodeLine() {
return codeLine;
}
//使用到了双分派,即首先在客户端程序中,将具体的参数作为参数传递到Engineer中(第一次分派)
//然后Engineer类调用作为参数的具体方法中的visit方法,同时将自己作为参数传入(第二次分派)
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Manager extends Staff {
private int product;
public Manager(String name, int kpi, int product) {
super(name, kpi);
this.product = product;
}
public int getProduct() {
return product;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
//抽象的访问者
interface Visitor {
//访问工程师类型
void visit(Engineer engineer);
//访问经理类型
void visit(Manager manager);
}
//具体的访问者
class CEO implements Visitor {
public void visit(Engineer engineer) {
System.out.println("工程师:" engineer.getName() ",kpi:" engineer.getKpi());
}
public void visit(Manager manager) {
System.out.println("经理:" manager.getName() ",kpi:" manager.getKpi());
}
}
class CTO implements Visitor {
public void visit(Engineer engineer) {
System.out.println("工程师:" engineer.getName() ",kpi:" engineer.getKpi() ",代码数:" engineer.getCodeLine());
}
public void visit(Manager manager) {
System.out.println("经理:" manager.getName() ",kpi:" manager.getKpi() ",产品数:" manager.getProduct());
}
}
//结构对象
class BusinessReport {
private List<Staff> staffList = new ArrayList<>();
public void addStaff(Staff staff) {
staffList.add(staff);
}
public void remove(Staff staff) {
staffList.remove(staff);
}
public void showReport(Visitor visitor) {
for (Staff staff : staffList) {
staff.accept(visitor);
}
}
}
class Client {
public static void main(String[] args) {
//构建报表
BusinessReport report = new BusinessReport();
report.addStaff(new Manager("张三", 84, 6));
report.addStaff(new Manager("李四", 93, 8));
report.addStaff(new Engineer("王五", 83, 63412));
report.addStaff(new Engineer("赵六", 90, 74832));
System.out.println("===CEO查看报表===");
report.showReport(new CEO());
System.out.println("===CTO查看报表===");
report.showReport(new CTO());
}
- 特点
1、访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。
2、访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。
3、访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。
10. 备忘录(Mementor)模式
- 定义
在不破坏封闭的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,从而可以将对象恢复到原先保存的状态,即就是为了在某一时刻把当前的状态记录下来,以后再恢复到那时的状态。
- 结构
1、发起人角色(Originator):负责创建、记录、恢复自身内部状态或数据。
2、备忘录角色(Mement):负责存储Originator的内部状态或数据,并且可以提供Originator的内部状态或数据。
3、备忘录管理员角色(Caretaker):负责管理备忘录,只负责将备忘录内容进行传递;
- 例子
class GameOriginator {
private int currentScore;
//将需要保存的状态封装再Memento里对外提供
public GameProgressMemento saveProcess() {
return new GameProgressMemento(currentScore);
}
//通过从外部接受得memento恢复状态
public void restoreProcess(GameProgressMemento memento) {
currentScore = memento.getScore();
}
//对内部状态的使用
public void playGame() {
System.out.println("------------------Start Game------------------");
System.out.println(String.format("当前分数为:%d", currentScore));
System.out.println("杀死小怪物得1分");
currentScore ;
System.out.println(String.format("总分为:%d", currentScore));
}
public void exitGame() {
System.out.println("退出游戏");
currentScore = 0;
System.out.println("------------------Game End------------------");
}
}
class GameProgressMemento {
private int score;
public GameProgressMemento(int score) {
this.score = score;
}
public int getScore() {
return score;
}
}
class GameCateTaker {
private List<GameProgressMemento> mementos = new ArrayList<>();
public void saveMemento(GameProgressMemento memento) {
mementos.add(memento);
}
public GameProgressMemento getMemento(int index) {
return mementos.get(index);
}
}
class Client {
public static void main(String[] args) {
GameOriginator originator = new GameOriginator();
GameCateTaker cateTaker = new GameCateTaker();
//玩游戏
originator.playGame();
originator.playGame();
//保存游戏进度
cateTaker.saveMemento(originator.saveProcess());
originator.exitGame();
//加载游戏
originator.restoreProcess(cateTaker.getMemento(0));
originator.playGame();
}
}
- 特点
优点:
1、提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
2、实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
3、简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
缺点:
资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
11. 解释器(Interpreter)模式
- 定义
给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
- 结构
1、抽象的表达式(AbstractExpression):生命一个抽象的解释操作,这个方法为抽象语法树中所有节点所共享。
2、终结符表达式(TerminalExpression):实现与文法中的终结符相关的解释操作。
3、非终结符表达式(NonTerminalExpression):为文法中的非终结符实现解释操作。
4、上下文(Context):是环境角色,含有解释器之外的全局信息。
- 例子
//抽象类表达式,通过map键值对,获取变量的值
abstract class Expression {
public abstract int interpreter(Map<String, Integer> var);
}
//变量解释器(终结符表达式)
class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> var) {
int result = var.get(key);
return result;
}
}
//运算符号表达式(非终结符表达式)
abstract class SymbolExpression extends Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
//处理减操作
@Override
public int interpreter(Map<String, Integer> var) {
int result = left.interpreter(var) - right.interpreter(var);
return result;
}
}
class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
int result = left.interpreter(var) right.interpreter(var);
return result;
}
}
//上下文
class Calculator {
//定义表达式
private Expression expression;
public Calculator(String expStr) {
analysisExp(expStr);
}
private void analysisExp(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
for (int i = 0; i < charArray.length; i ) {
char c = charArray[i];
switch (c) {
case ' ':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[ i]));
stack.push(new AddExpression(left, right));
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[ i]));
stack.push(new SubExpression(left, right));
break;
default:
stack.push(new VarExpression(String.valueOf(c)));
break;
}
}
this.expression = stack.pop();
}
public int run (Map<String, Integer> var) {
int result = this.expression.interpreter(var);
return result;
}
}
class Client {
public static void main(String[] args) {
String exp = "a b-c";
Calculator calculator = new Calculator(exp);
Map<String, Integer> map = ImmutableMap.of("a", 3, "b", 5, "c", 4);
System.out.println(exp " = " calculator.run(map));
}
}
- 特点
优点
1、扩展性好,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2、容易实现,在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点
1、执行效率较低,解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
2、会引起类膨胀,解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
3、可应用的场景比较少,在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。