Java 中一般认为有23种设计模式,当然暂时不需要所有的都会,但是其中常见的几种设计模式应该去掌握。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1. 单例模式
所谓的单例设计指的是一个类只允许产生一个实例化对象。
最好理解的一种设计模式,分为懒汉式和饿汉式。
2. 工厂设计模式
工厂模式分为工厂方法模式和抽象工厂模式。
工厂方法模式
工厂方法模式:
1. 工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
2. 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
3. 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可
2.1 普通工厂模式
public class FactoryPattern {
public static void main(String[] args) {
Sender sender = produce("mail");
sender.Send();
}
public static Sender produce(String str) {
if ("mail".equals(str)) {
return new MailSender();
} else if ("sms".equals(str)) {
return new SmsSender();
} else {
System.out.println("输入错误...");
return null;
}
}
}
2. 2多个工厂方法模式
该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
class SendFactory {
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
public class FactoryPattern {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
2.3 静态工厂方法模式
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
2.4 抽象工厂模式
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?
那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
(需要分别创建工厂类-然后都实现同一个接口,个人觉得太麻烦了,既然都重新建立工厂类了,不如直接调用原类??)
3. 建造者模式
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。{???}
package com.common.designPattern;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: LiuWang
* @Created: 2018/8/6 17:47
*/
abstract class Builder {
/**
* 第一步:装CPU
*/
public abstract void buildCPU();
/**
* 第二步:装主板
*/
public abstract void buildMainBoard();
/**
* 第三步:装硬盘
*/
public abstract void buildHD();
/**
* 获得组装好的电脑
* @return
*/
public abstract Computer getComputer();
}
/**
* 装机人员装机
*/
class Director {
public void Construct(Builder builder) {
builder.buildCPU();
builder.buildMainBoard();
builder.buildHD();
}
}
/**
* 具体的装机人员
*/
class ConcreteBuilder extends Builder {
Computer computer = new Computer();
@Override
public void buildCPU() {
computer.Add("装CPU");
}
@Override
public void buildMainBoard() {
computer.Add("装主板");
}
@Override
public void buildHD() {
computer.Add("装硬盘");
}
@Override
public Computer getComputer() {
return computer;
}
}
class Computer {
/**
* 电脑组件集合
*/
private List<String> parts = new ArrayList<String>();
public void Add(String part) {
parts.add(part);
}
public void print() {
for (int i = 0; i < parts.size(); i++) {
System.out.println("组件:" + parts.get(i) + "装好了...");
}
System.out.println("电脑组装完毕...");
}
}
public class BuilderPattern {
public static void main(String[] args) {
Director director = new Director();
Builder builder = new ConcreteBuilder();
director.Construct(builder);
Computer computer = builder.getComputer();
computer.print();
}
}
4. 适配器设计模式
适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题。主要分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
package com.common.designPattern;
class Source {
public void method1() {
System.out.println("This is original method...");
}
}
interface Targetable {
/**
* 与原类中的方法相同
*/
public void method1();
/**
* 新类的方法
*/
public void method2();
}
class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("This is the targetable method...");
}
}
class AdapterPattern {
public static void main(String[] args) {
Targetable targetable = new Adapter();
targetable.method1();
targetable.method2();
}
}
2. 对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter 类作修改,这次不继承Source 类,而是持有Source 类的实例,以达到解决兼容性的问题。
class Source {
public void method1() {
System.out.println("This is original method...");
}
}
interface Targetable {
/**
* 与原类中的方法相同
*/
public void method1();
/**
* 新类的方法
*/
public void method2();
}
class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source) {
super();
this.source = source;
}
@Override
public void method1() {
source.method1();
}
@Override
public void method2() {
System.out.println("This is the targetable method...");
}
}
public class AdapterPattern {
public static void main(String[] args) {
Source source = new Source();
Targetable targetable = new Wrapper(source);
targetable.method1();
targetable.method2();
}
}
3. 接口的适配器模式
接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
/**
* 定义端口接口,提供通信服务
*/
interface Port {
/**
* 远程SSH端口为22
*/
void SSH();
/**
* 网络端口为80
*/
void NET();
/**
* Tomcat容器端口为8080
*/
void Tomcat();
/**
* MySQL数据库端口为3306
*/
void MySQL();
}
/**
* 定义抽象类实现端口接口,但是什么事情都不做
*/
abstract class Wrapper implements Port {
@Override
public void SSH() {
}
@Override
public void NET() {
}
@Override
public void Tomcat() {
}
@Override
public void MySQL() {
}
}
/**
* 提供聊天服务
* 需要网络功能
*/
class Chat extends Wrapper {
@Override
public void NET() {
System.out.println("Hello World...");
}
}
/**
* 网站服务器
* 需要Tomcat容器,Mysql数据库,网络服务,远程服务
*/
class Server extends Wrapper {
@Override
public void SSH() {
System.out.println("Connect success...");
}
@Override
public void NET() {
System.out.println("WWW...");
}
@Override
public void Tomcat() {
System.out.println("Tomcat is running...");
}
@Override
public void MySQL() {
System.out.println("MySQL is running...");
}
}
public class AdapterPattern {
private static Port chatPort = new Chat();
private static Port serverPort = new Server();
public static void main(String[] args) {
// 聊天服务
chatPort.NET();
// 服务器
serverPort.SSH();
serverPort.NET();
serverPort.Tomcat();
serverPort.MySQL();
}
}
5. 装饰模式
顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
interface Shape {
void draw();
}
/**
* 实现接口的实体类
*/
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle...");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle...");
}
}
/**
* 创建实现了 Shape 接口的抽象装饰类。
*/
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
}
@Override
public void draw() {
decoratedShape.draw();
}
}
/**
* 创建扩展自 ShapeDecorator 类的实体装饰类。
*/
class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
}
/**
* 使用 RedShapeDecorator 来装饰 Shape 对象。
*/
public class DecoratorPattern {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
6. 策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可(????)
/**
* 抽象算法的策略类,定义所有支持的算法的公共接口
*/
abstract class Strategy {
/**
* 算法方法
*/
public abstract void AlgorithmInterface();
}
/**
* 具体算法A
*/
class ConcreteStrategyA extends Strategy {
//算法A实现方法
@Override
public void AlgorithmInterface() {
System.out.println("算法A的实现");
}
}
/**
* 具体算法B
*/
class ConcreteStrategyB extends Strategy {
/**
* 算法B实现方法
*/
@Override
public void AlgorithmInterface() {
System.out.println("算法B的实现");
}
}
/**
* 具体算法C
*/
class ConcreteStrategyC extends Strategy {
@Override
public void AlgorithmInterface() {
System.out.println("算法C的实现");
}
}
/**
* 上下文,维护一个对策略类对象的引用
*/
class Context {
Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void contextInterface(){
strategy.AlgorithmInterface();
}
}
/**
* 客户端代码:实现不同的策略
*/
public class StrategyPattern {
public static void main(String[] args) {
Context context;
context = new Context(new ConcreteStrategyA());
context.contextInterface();
context = new Context(new ConcreteStrategyB());
context.contextInterface();
context = new Context(new ConcreteStrategyC());
context.contextInterface();
}
}
7. 代理模式
代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。
通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。
7.1 静态代理
静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。
/**
* 代理类和被代理类共同实现的接口
*/
interface IService {
void service();
}
/**
* 被代理类
*/
class Service implements IService{
@Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
}
/**
* 代理类
*/
class ProxyService implements IService{
/**
* 持有被代理对象的引用
*/
private IService service;
/**
* 默认代理Service类
*/
public ProxyService() {
this.service = new Service();
}
/**
* 也可以代理实现相同接口的其他类
* @param service
*/
public ProxyService(IService service) {
this.service = service;
}
@Override
public void service() {
System.out.println("开始执行service()方法");
service.service();
System.out.println("service()方法执行完毕");
}
}
//测试类
public class ProxyPattern {
public static void main(String[] args) {
IService service = new Service();
//传入被代理类的对象
ProxyService proxyService = new ProxyService(service);
proxyService.service();
}
}
7.2 动态代理
JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。
动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。
创建动态代理对象的步骤:
1. 指明一系列的接口来创建一个代理对象
2. 创建一个调用处理器(InvocationHandler)对象
3. 将这个代理指定为某个其他对象的代理对象
4. 在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类和被代理类共同实现的接口
*/
interface IService {
void service();
}
class Service implements IService{
@Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
}
class ServiceInvocationHandler implements InvocationHandler {
/**
* 被代理的对象
*/
private Object srcObject;
public ServiceInvocationHandler(Object srcObject) {
this.srcObject = srcObject;
}
@Override
public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
System.out.println("开始执行"+method.getName()+"方法");
//执行原对象的相关操作,容易忘记
Object returnObj = method.invoke(srcObject,args);
System.out.println(method.getName()+"方法执行完毕");
return returnObj;
}
}
public class ProxyPattern {
public static void main(String[] args) {
IService service = new Service();
Class<? extends IService> clazz = service.getClass();
IService proxyService = (IService) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), new ServiceInvocationHandler(service));
proxyService.service();
}
}
8 观察者模式
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
在观察者模式中有如下角色:
Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
2.观察者模式简单实现
观察者模式这种发布-订阅的形式我们可以拿微信公众号来举例,假设微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号,当这个公众号更新时就会通知这些订阅的微信用户。好了我们来看看用代码如何实现:
抽象观察者(Observer)
里面定义了一个更新的方法:
public interface Observer {
public void update(String message);
}
具体观察者(ConcrereObserver)
微信用户是观察者,里面实现了更新的方法:
public class WeixinUser implements Observer {
// 微信用户名
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "-" + message);
}
}
抽象被观察者(Subject)
抽象主题,提供了attach、detach、notify三个方法
public interface Subject {
/**
* 增加订阅者
* @param observer
*/
public void attach(Observer observer);
/**
* 删除订阅者
* @param observer
*/
public void detach(Observer observer);
/**
* 通知订阅者更新消息
*/
public void notify(String message);
}
具体被观察者(ConcreteSubject)
微信公众号是具体主题(具体被观察者),里面存储了订阅该公众号的微信用户,并实现了抽象主题中的方法:
public class SubscriptionSubject implements Subject {
//储存订阅公众号的微信用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message);
}
}
}
客户端调用
public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建微信用户
WeixinUser user1=new WeixinUser("杨影枫");
WeixinUser user2=new WeixinUser("月眉儿");
WeixinUser user3=new WeixinUser("紫轩");
//订阅公众号
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);
//公众号更新发出消息给订阅的微信用户
mSubscriptionSubject.notify("刘望舒的专栏更新了");
}
}
3.使用观察者模式的场景和优缺点
使用场景
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
9 责任链模式
场景构建
某公司员工要出差,出差这笔费用需要向上级汇报并申请,但是针对出差费过多的情况,
可能会出现这位员工的直属上级没有权限批准,可能这位直属领导会向他的上级进行汇报申请,
这样依次类推直到某个领导能有权限批准这笔费用为止!
逻辑图如下
==员工== ->==领导A==->==领导B==->==领导C==>->......->==领导N==
package com.common.designPattern.daili.zerenlian;
/**
* 员工A要出差
*/
public class Person {
private int money;
/**
* 设置出差需要非费用
*/
public void setMoney(int money) {
this.money = money;
}
/**
* 获取需要申请的出差费用
*/
public int getMoney() {
return money;
}
/**
* 向上级回报
*/
public void getApply() {
System.out.print("老大我需要的出差费用是" + getMoney());
}
/**
* 申请500已下的金额可以批,大于500会向上级回报
*/
public static class Leader500 {
public void handler(Person person) {
person.getApply();
System.out.print(" 我是Leader500 你需要申请的" + person.getMoney() + "我批准了");
}
}
/**
* 申请1000已下的金额可以批,大于1000会向上级回报
*/
public static class Leader1000 {
public void handler(Person person) {
person.getApply();
System.out.print(" 我是Leader1000 你需要申请的" + person.getMoney() + "我批准了");
}
}
/**
* 申请1500已下的金额可以批,大于1500会向上级回报
*/
public static class Leader1500 {
public void handler(Person person) {
person.getApply();
System.out.print(" 我是Leader1500 你需要申请的" + person.getMoney() + "我批准了");
}
}
/**
* 申请2000已下的金额可以批,大于2000会向上级回报
*/
public static class Leader2000 {
public void handler(Person person) {
person.getApply();
System.out.print(" 我是Leader2000 你需要申请的" + person.getMoney() + "我批准了");
}
}
public static void main(String[] args) {
//领导们
Leader500 leader500 = new Leader500();
Leader1000 leader1000 = new Leader1000();
Leader1500 leader1500 = new Leader1500();
Leader2000 leader2000 = new Leader2000();
//申请人
Person person = new Person();
person.setMoney(2000);
if (person.getMoney() <= 500) {
leader500.handler(person);
} else if (person.getMoney() <= 1000) {
leader1000.handler(person);
} else if (person.getMoney() <= 1500) {
leader1500.handler(person);
} else if (person.getMoney() <= 2000) {
leader2000.handler(person);
}
}
}
优化
看到这里是不是很简单?虽然是简单,但是并不符合我们写代码的规范,所以我们先优化一下代码,优化代码的出发点如下:
- 具体是谁申请出差费用我们不管,我们只管申请出差需要做那些事!
- 具体是谁审批我们不需要纠结,我们只管审批需要做哪些事
针对以上两点,我们可以把申请人抽象成类,把审批人做成接口
申请人的抽象类如下:
/**
* 将申请人的操作抽象化 不需要关心是谁来申请出差费用 只关心出差申要做的事
*/
public abstract class ApplyInfo {
int money;
public abstract void setMoney(int money);
public abstract int getMoney();
public void getApply() {
System.out.print("老大我需要的出差费用是" + getMoney());
}
}
审批人的接口如下:
/**
* 申请人
*/
public class Person extends ApplyInfo {
@Override
public void setMoney(int money) {
super.money = money;
}
@Override
public int getMoney() {
return super.money;
}
}
领导 必须抽象成类:
/**
* 将领导需要做的事抽象成类
*/
public abstract class LeaderInfo {
//当前领导审核的金额
int auditMoney;
//当前领导的上级领导
LeaderInfo superiorLeader;
//设置当前领导能审批的额度
public abstract void setCurrentMoney(int money);
//当前领导收到申请后处理的事情
public abstract void handler(ApplyInfo applyInfo);
//设置当前领导的上一级领导
public abstract void setSuperiorLeader(LeaderInfo superiorLeader);
//执行审批流程
public void dealInfo(ApplyInfo applyInfo) {
if (applyInfo.money <= auditMoney) {
handler(applyInfo);
} else {
superiorLeader.dealInfo(applyInfo);
}
}
}
领导
/**
* 申请500已下的金额可以批,大于500会向上级回报
*/
public class Leader500 extends LeaderInfo {
@Override
public void setCurrentMoney(int money) {
super.auditMoney = money;
}
@Override
public void handler(ApplyInfo applyInfo) {
applyInfo.getApply();
System.out.print(" 我是Leader500 你需要申请的" + applyInfo.getMoney() + "我批准了");
}
@Override
public void setSuperiorLeader(LeaderInfo superiorLeader) {
super.superiorLeader = superiorLeader;
}
}
public static void main(String[] args) {
//领导们
Leader500 leader500 = new Leader500();
Leader1000 leader1000 = new Leader1000();
Leader1500 leader1500 = new Leader1500();
Leader2000 leader2000 = new Leader2000();
leader500.setCurrentMoney(500);
leader1000.setCurrentMoney(1000);
leader1500.setCurrentMoney(1500);
leader2000.setCurrentMoney(2000);
leader500.setSuperiorLeader(leader1000);
leader1000.setSuperiorLeader(leader1500);
leader1500.setSuperiorLeader(leader2000);
//申请人
Person person = new Person();
person.setMoney(2000);
leader500.dealInfo(person);
}
总结
责任链模式非常简单,就是使多个对象都有计划处理请求,从而避免请求的发送者和接受者之间的耦合关系。将
这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
10 中介者模式
中介者模式(Mediator pattern) : 使用中介者模式来集中相关对象之间复杂的沟通和控制方式,使得这些对象不必相互明显引用。从而使它们可以较松散地耦合。当这些对象中的某些对象之间的相互作用发生改变时,不会立即影响到其他的一些对象之间的相互作用。从而保证这些相互作用可以彼此独立地变化。
意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。
关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。
应用实例: 1、中国加入 WTO 之前是各个国家相互贸易,结构复杂,现在是各个国家通过 WTO 来互相贸易。 2、机场调度系统。 3、MVC 框架,其中C(控制器)就是 M(模型)和 V(视图)的中介者。
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会庞大,变得复杂难以维护。
使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
注意事项:不应当在职责混乱的时候使用。
角色说明:
抽象中介者角色(AbstractMediator):定义出同事对象到中介者对象的接口,其中主要方法是一个(或多个)事件方法。
具体中介者角色(ConcreteMediator):实现抽象中介者中所声明的事件方法。具体中介者直销所有的具体同事类,并负责具体的协调各个同事对象的交互关系。
抽象同事类角色(AbstractColleague):定义出红接着到同事对象的接口。同事对象只知道中介者,而不知道其余的同事对象。
具体同事类角色(ConcreteColleague):所有的具体同事类均从抽象同事类继承而来。实现自己的业务,在需要与其他同事通信的时候,就与持有的中介者通信,中介者会负责与其他的同时交互。
抽象中介者:抽象同事类:
public interface AbstractMediator {
//中介者通过此方法来改变同事B
public void aChangedB(String str);
//中介者通过此方法来改变同事A
public void bChangedA(String str);
}
public abstract class AbstractColleague {
//同事对象在改变的时候,通知中介者,并传递影响值
public abstract void setString(String str, AbstractMediator am);
//同事对象提供一个接口接收中介者传递的影响值
public abstract void changeString(String str);
}
public class ConcreteColleagueA extends AbstractColleague {
// 同事A的字符串属性
private String strAttribute;
// 构造函数,构造字符串属性
public ConcreteColleagueA(String str) {
this.strAttribute = str;
}
/**
* 被改变的函数
*/
@Override
public void changeString(String str) {
this.strAttribute = this.strAttribute + "这是同事B对我的影响:" + str;
}
/**
* 改变时,通知中介者改变其他同事
*/
@Override
public void setString(String str, AbstractMediator am) {
am.aChangedB(str);
}
public String getStrAttribute() {
return strAttribute;
}
}
同事B
public class ConcreteColleagueB extends AbstractColleague {
private String strAttribute;
public ConcreteColleagueB(String str) {
this.strAttribute = str;
}
@Override
public void changeString(String str) {
this.strAttribute = this.strAttribute + "这是同事A对我的影响:" + str;
}
@Override
public void setString(String str, AbstractMediator am) {
am.bChangedA(str);
}
public String getStrAttribute() {
return strAttribute;
}
}
中介
public class ConcreteMediator implements AbstractMediator {
// 持有并维护同事A
private ConcreteColleagueA colleagueA;
// 持有并维护同事B
private ConcreteColleagueB colleagueB;
// 传入同事A的对象
public void setColleagueA(ConcreteColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
// 传入同事B的对象
public void setColleagueB(ConcreteColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
/**
* 收到同事A的改变,通知同事B改变
*/
@Override
public void aChangedB(String str) {
colleagueB.changeString(str);
}
/**
* 收到同事B的改变,通知同事A改变
*/
@Override
public void bChangedA(String str) {
colleagueA.changeString(str);
}
}
客户端测试类:
public class Client {
public static void main(String[] args) {
// 实例化同事A、B
ConcreteColleagueA colleagueA = new ConcreteColleagueA("大家好,我是同事A。");
ConcreteColleagueB colleagueB = new ConcreteColleagueB("大家好,我是同事B。");
// 同时把同事A、B传入给中介者
ConcreteMediator am = new ConcreteMediator();
am.setColleagueA(colleagueA);
am.setColleagueB(colleagueB);
// 同事A影响同事B
System.out.println(colleagueB.getStrAttribute());
colleagueA.setString("同事A传递给同事B的值", am);
System.out.println(colleagueB.getStrAttribute());
System.out.println("==========================");
// 同事B影响同事A
System.out.println(colleagueA.getStrAttribute());
colleagueB.setString("同事B传递给同事A的值", am);
System.out.println(colleagueA.getStrAttribute());
}
}
中介者模式的优点:
使用中介者模式可以把对个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合。
中介者模式可以将原先多对多的同事对象关系变成中介者对象一对多同事对象的关系,这样会让对象之间的关系更容易理解和实现。
同事对象之间的交互都被封装到中介者对象里面集中管理,集中了控制交互。当交互发生改变时,着重修改的是中介者对象。当需要扩展中介者对象时,其他同事对象不需要做修改。
缺点:
过度集中化,这是中介者模式潜在的缺点。如果同事对象多了,交互也复杂了。那么这些交互全部集中到中介者对象中,会导致中介者对象十分臃肿,难以管理和维护。
11 访问者模式
访问者模式介绍
最复杂的设计模式,并且使用频率不高,《设计模式》的作者评价为:大多情况下,你不需要使用访问者模式,但是一旦需要使用它时,那就真的需要使用了。
访问者模式是一种将数据操作和数据结构分离的设计模式。(觉得太抽象,可以看下面的例子)。
详情
https://www.jianshu.com/p/1f1049d0a0f4