设计模式 Day07
文章总结自B站尚硅谷
1. 命令模式
在命令模式中,会将一个请求封装成为一个对象,以便使用不同参数来表示不同的请求,也支持可撤销命令。
案例:通过一个手机APP控制家里的智能家电。
引用场景:向某些对象发送请求,但是不需要知道接收者是谁,只需要在程序运行时指定具体的接收者。
优势:消除了请求者与请求接收者之间的耦合,让对象之间的调用更加灵活,实现解耦。
电灯开关接口
/**
* @date 2020/8/30 8:25
*/
public class LightReceiver {
public void on(){
System.out.println(" 电灯打开了 ");
}
public void off(){
System.out.println(" 电灯关闭了 ");
}
}
无操作类
/**
* 没有任何命令 用于初始化按钮 可以省掉对空判断
* @date 2020/8/30 8:32
*/
public class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
开灯类/关灯类
/**
* @date 2020/8/30 8:23
*/
public class LightOnCommand implements Command{
LightReceiver lightReceiver;
public LightOnCommand(LightReceiver lightReceiver) {
this.lightReceiver = lightReceiver;
}
@Override
public void execute() {
lightReceiver.on();
}
@Override
public void undo() {
lightReceiver.off();
}
}
控制器:
/**
* @date 2020/8/30 8:36
*/
public class RemoteController {
// 开
Command[] onCommands;
// 关
Command[] offCommands;
// 撤销
Command undoCommand;
// 对按钮初始化
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < 5; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
// 给按钮设置需要的命令
public void setCommand(int no,Command onCommand,Command offCommand){
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
// 按下开的按钮
public void onButtonWasPushed(int no){
// 找到按下的开的按钮,并且调用对应的方法
onCommands[no].execute();
// 记录操作,用于撤销
undoCommand = onCommands[no];
}
// 按下关的按钮
public void offButtonWasPushed(int no){
// 找到按下的关的按钮,并且调用对应的方法
offCommands[no].execute();
// 记录操作,用于撤销
undoCommand = offCommands[no];
}
// 按下撤销按钮
public void undoButtonWasPushed(){
undoCommand.undo();
}
}
客户端:
/**
* @date 2020/8/30 8:45
*/
public class Client {
public static void main(String[] args) {
// 电灯对象
LightReceiver lightReceiver = new LightReceiver();
// 开关灯的对象
LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
// 遥控器对象
RemoteController remoteController = new RemoteController();
// 给遥控器设置相关命令
remoteController.setCommand(0,lightOnCommand,lightOffCommand);
System.out.println("打开灯");
remoteController.onButtonWasPushed(0);
System.out.println("关闭灯");
remoteController.offButtonWasPushed(0);
System.out.println("撤销");
remoteController.undoButtonWasPushed();
}
}
在Spring的JDBCTemplate中使用到了
注意事项和细节:
- 将发起请求的对象与执行请求的对象解耦,发起请求的对象是调用者,调用者只要调用命令对象的
execute()
方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现,命令对象只会负责让接收者执行请求的动作。请求发起者和请求执行者之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。 - 设计一个命令队列,只要把命令对象放到队列中,就可以多线程的执行命令。
- 容易实现对请求的撤销和重做。
- 缺点:可能导致系统有过多的具体命令类,增加了系统复杂度。
2. 访问者模式
封装一些作用于某种数据结构的各元素的操作,它可以不改变数据结构的前提下定义作用于这些元素的新的操作。主要将数据结构和数据操作分离,解决数据结构和耦合性的问题。工作原理:在被访问的类里面加一个对外提供接待访问者的接口。主要应用于需要对一个对象结构中的对象进行很多不同操作。
案例:测评系统:对歌手进行评测。
action:
/**
* @date 2020/8/30 9:20
*/
public abstract class Action {
// 得到男性的测评结果
public abstract void getManResult(Man man);
// 得到女性的测评结果
public abstract void getWomanResult(Woman woman);
}
成功类:
/**
* @date 2020/8/30 9:23
*/
public class Success extends Action {
@Override
public void getManResult(Man man) {
System.out.println(" 男-成功 ");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(" 女-成功");
}
}
失败类:
/**
* @date 2020/8/30 9:24
*/
public class Fail extends Action {
@Override
public void getManResult(Man man) {
System.out.println(" 男-失败 ");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(" 女-失败 ");
}
}
person:
/**
* @date 2020/8/30 9:22
*/
public abstract class Person {
// 让访问者访问
public abstract void accept(Action action);
}
男:
/**
* 使用到了双分派,即首先在客户端程序中,将具体的状态作为参数传递到类中(第一次),
* 该类调用了作为参数的具体方法,同时将自己作为参数传入(第二次)。
* @date 2020/8/30 9:21
*/
public class Man extends Person {
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
女:
/**
* @date 2020/8/30 9:22
*/
public class Woman extends Person {
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}
数据结构:
/**
* 数据结构,管理人
* @date 2020/8/30 9:29
*/
public class ObjectStructure {
private List<Person> personList = new LinkedList<>();
// 添加到list
public void attach(Person person){
personList.add(person);
}
// 移除
public void detach(Person person){
personList.remove(person);
}
// 显示测评情况
public void display(Action action){
for (Person person : personList) {
person.accept(action);
}
}
}
客户端:
/**
* @date 2020/8/30 9:32
*/
public class Client {
public static void main(String[] args) {
ObjectStructure structure = new ObjectStructure();
structure.attach(new Man());
structure.attach(new Man());
structure.attach(new Woman());
Success success = new Success();
structure.display(success);
Fail fail = new Fail();
structure.display(fail);
}
}
双分派:
指不管类怎么变化,都能找到期望的方法运行,意味着得到执行的操作取决于请求的种类和两个接收者的类型。
新增Wait
类
/**
* @date 2020/8/30 9:42
*/
public class Wait extends Action{
@Override
public void getManResult(Man man) {
System.out.println(" 男-待定 ");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println(" 女-待定 ");
}
}
在不修改其它代码的前提下就可以实现新增一个状态。
优点:
- 符合单一职责原则,有优秀的扩展性,灵活性高
- 对功能进行统一,适用于数据结构相对稳定的系统。
缺点:
- 具体元素对细节公布,也就是说访问者关注了其它类的内部细节,这是迪米特法则不建议的,这样造成具体元素变更比较困难。
- 违背了依赖倒置原则,访问者依赖的是具体元素而不是抽象元素。
3. 迭代器模式
提供一个统一的接口,用一致的方法遍历集合元素,不需要知道集合的底层表示。
如果集合元素使用多种不同的方式实现的,当客户端需要使用多种遍历方式遍历集合元素时,还会暴露元素的内部结构,就可以考虑使用迭代器模式。
案例:学院展示,一个学校有多个学院,一个学院又有多个专业。
学校:
/**
* @date 2020/8/30 14:53
*/
public interface College {
// 获取名字
public String getName();
// 添加系
public void addDepartment(String name,String desc);
// 返回用来遍历的迭代器
public Iterator createIterator();
}
学院迭代器:
/**
* 计算机学院 迭代器
* @date 2020/8/30 14:39
*/
public class ComputerCollegeIterator implements Iterator {
// 需要知道department是以什么样的集合存放的。
Department[] departments;
int position = 0; // 遍历位置
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
/**
* 判断是否还有下一个元素
* @return boolean
*/
@Override
public boolean hasNext() {
if (position >= departments.length || departments[position] == null){
return false;
}else {
return true;
}
}
/**
* 遍历数组
* @return
*/
@Override
public Object next() {
Department department = departments[position];
position += 1;
return department;
}
@Override
public void remove(){
}
}
/**
* 信息学院 迭代器
* @date 2020/8/30 14:47
*/
public class InfoCollegeIterator implements Iterator {
// 以list存放
List<Department> departments;
int index = -1;
public InfoCollegeIterator(List<Department> departments) {
this.departments = departments;
}
/**
* 判断集合中还有没有下一个元素
* @return boolean
*/
@Override
public boolean hasNext() {
if (index >= departments.size() -1){
return false;
}else {
index += 1;
return true;
}
}
/**
* 遍历list
* @return list
*/
@Override
public Object next() {
return departments.get(index);
}
@Override
public void remove() {
}
}
学院:
/**
* 计算机学院
* @date 2020/8/30 14:56
*/
public class ComputerCollege implements College {
Department[] departments;
int numOfDepartment = 0; // 保存当前数组的个数
public ComputerCollege() {
departments = new Department[5];
addDepartment("java","javaavava");
addDepartment("java","javaavava");
addDepartment("java","javaavava");
addDepartment("java","javaavava");
}
@Override
public String getName() {
return "计算机学院";
}
@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments[numOfDepartment] = department;
numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}
/**
* 信息学院
* @date 2020/8/30 15:06
*/
public class InfoCollege implements College{
List<Department> list;
public InfoCollege() {
list = new ArrayList<>();
addDepartment("信息安全","信息安全");
addDepartment("信息安全","信息安全");
addDepartment("信息安全","信息安全");
addDepartment("信息安全","信息安全");
addDepartment("信息安全","信息安全");
addDepartment("信息安全","信息安全");
}
@Override
public String getName() {
return "信息学院";
}
@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
list.add(department);
}
@Override
public Iterator createIterator() {
return new InfoCollegeIterator(list);
}
}
系:
public class Department {
private String name;
private String desc;
public Department(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
输出整合:
/**
* @date 2020/8/30 15:09
*/
public class OutPutImp1 {
// 学院集合
List<College> colleges;
public OutPutImp1(List<College> colleges) {
this.colleges = colleges;
}
// 遍历学院
public void printCollege(){
// list 已经 实现了 iterator接口
Iterator<College> iterator = colleges.iterator();
while (iterator.hasNext()){
College college = iterator.next();
System.out.println("============== "+college.getName()+" ==============");
printDepartment(college .createIterator()); // 得到对应的迭代器
}
}
// 学院输出系
public void printDepartment(Iterator iterator){
while (iterator.hasNext()){
Department departments = (Department) iterator.next();
System.out.println(departments.getName());
}
}
}
客户端:
/**
* @date 2020/8/30 15:14
*/
public class Client {
public static void main(String[] args) {
ArrayList<College> colleges = new ArrayList<>();
ComputerCollege computerCollege = new ComputerCollege();
InfoCollege infoCollege = new InfoCollege();
System.out.println(infoCollege.list);
colleges.add(computerCollege);
colleges.add(infoCollege);
OutPutImp1 outPutImp1 = new OutPutImp1(colleges);
outPutImp1.printCollege();
}
}
在JDK的ArrayList中使用到了
优点:
- 提供一个统一的方法遍历对象,客户不用再考虑集合的类型,使用一种方法就可以遍历对象。
- 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
- 把管理对象和聚合对象分开,这样的话如果改变集合的话,只影响到聚合对象,如果遍历方式改变的话,只影响到了迭代器。
- 当要展示一组相似对象,或者响头对象的时候适合使用迭代器模式。
缺点:每个聚合对象都要一个迭代器,不好进行管理。
4. 观察者模式
案例:天气预报:将每天测量到的温度、湿度、气压发布出去。可以添加/移除第三方。客户端也可以主动的调用更新方法使得第三方也可以看到最新的内容。
/**
* 观察者管理中心
* @date 2020/8/30 16:00
*/
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}
/**
* 观察者使用中心
* @date 2020/8/30 16:02
*/
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
天气:
/**
* @date 2020/8/30 16:09
*/
public class WeatherData implements Subject {
private float temperature;
private float pressure;
private float humidity;
// 观察者
private ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public void dataChange(){
notifyObserver();
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
/**
* 注册观察者
* @param observer
*/
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
/**
* 移除观察者
* @param observer
*/
@Override
public void removeObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
/**
* 遍历所有的观察者 并通知
*
*/
@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
observers.get(i).update(getTemperature(),getPressure(),getHumidity());
}
}
}
调用者:
/**
* @date 2020/8/30 16:04
*/
public class CurrentCondition implements Observer{
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
@Override
public void update(float temperature, float pressure, float humidity) { this.temperature = temperature;
this.pressure = pressure; this.humidity = humidity; display();
}
// 显 示
public void display() {
System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***");
}
}
客户端:
/**
* @date 2020/8/30 16:20
*/
public class Client {
public static void main(String[] args) {
// 创建WeatherData
WeatherData weatherData = new WeatherData();
// 观察者
CurrentCondition currentCondition = new CurrentCondition();
BaiduSite baiduSite = new BaiduSite();
// 注册观察者
weatherData.registerObserver(currentCondition);
weatherData.registerObserver(baiduSite);
System.out.println("通知:看信息!");
weatherData.setData(10.3f,100f,30f);
}
}
这样如果想要新增调用者就只需要新增调用者的类即可
/**
* @date 2020/8/30 16:23
*/
public class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
// 显 示
public void display() {
System.out.println("百度网站的最新消息"+"\n"+"***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***");
}
}
在JDK的Observable中使用了观察者模式
5. 中介者模式
用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地互相引用,从而使得耦合度松散,并且可以独立的改变他们之间的交互。
案例:智能家庭,家庭中有很多智能设备,使用开关来打开/关闭他们。
注意事项和细节:
- 多个类互相耦合,形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦。
- 中介者承担了较多的职责,一旦出问题,会影响整个系统。
- 设计不当会使中介者对象过于复杂。