常用设计模式在实际开发中的应用
一、设计模式的定义
设计模式是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
二、设计模式的分类
根据范围可分为类模式和对象模式
根据目的可分为创建型模式、结构型模式、行为型模式
创建型模式:将对象的创建和使用分离,降低系统耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。
结构型模式:用来描述如何将类或对象按某种布局组成更大的结构,类结构型模式采用继承机制来组织接口和类,对象结构型模式采用组合或聚合来组织对象。
行为型模式:用来描述多个类或对象之间怎么相互协作共同完成单个对象无法完成的任务,类行为模式采用继承机制在类间分配行为,对象行为型模式采用组合或聚合在对象间分配行为。
三、面向对象七大原则
开闭原则:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
里式替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
依赖倒置原则:要面向接口编程,不要面向实现编程。
单一职责原则:规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。
接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则:又叫最少知识原则,如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。
合成复用原则:要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
四、设计模式在实际开发中的应用
1、原型模式
定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
- 场景1:公司里的实习生每天都要写实习总结,可以采用原型模式创建一个模板,每次写总结时可以复制一个模板,修改部分内容即可。
代码
import java.util.Date;
//具体原型类
public class Summary implements Cloneable{
private String name;//姓名
private String department;//部门
private String status;//职位
private Date date;//日期
private String info;//工作日志
private String plan;//工作计划
private String problem;//遇到的问题
public Summary(String name, String department, String status, Date date, String info, String plan, String problem) {
this.name = name;
this.department = department;
this.status = status;
this.date = date;
this.info = info;
this.plan = plan;
this.problem = problem;
System.out.println("总结模板创建成功");
}
public void setName(String name) {
this.name = name;
}
public void setDepartment(String department) {
this.department = department;
}
public void setStatus(String status) {
this.status = status;
}
public void setDate(Date date) {
this.date = date;
}
public void setInfo(String info) {
this.info = info;
}
public void setPlan(String plan) {
this.plan = plan;
}
public void setProblem(String problem) {
this.problem = problem;
}
public void display() {
System.out.println("姓名:"+name + ",部门:"+department+",职位:"+status+",时间:"+date);
System.out.println("工作总结");
System.out.println(info);
System.out.println("工作计划");
System.out.println(plan);
System.out.println("遇到的问题");
System.out.println(problem);
}
@Override
public Object clone() throws CloneNotSupportedException {
System.out.println("总结模板拷贝成功");
return super.clone();
}
}
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class main {
public static void main(String[] args) throws CloneNotSupportedException {
Summary summary=new Summary("张三","研发三部","Java开发工程师",new Date(),
"1.学习k8s文档\n" +
"2.阅读桩桩项目代码及项目整体技术架构\n" +
"3.深入理解剩余11种设计模式并写demo",
"1.继续熟悉相关文档\n" +
"2.继续阅读桩桩项目代码及项目整体技术架构\n" +
"3.将设计模式应用到实际场景中,将多种设计模式混合使用并写demo\n" +
"4.完成组长布置的相应任务","没有问题");
summary.display();
System.out.println("----------------------------");
summary=(Summary)summary.clone();
summary.setDate(new Date());
summary.setInfo("1.熟悉开发文档\n" +
"2.熟悉GitLab使用规范\n" +
"3.熟悉k8s文档,对k8s有一个初步了解\n" +
"4.向导师请教文档中遇到的问题");
summary.setPlan("1.继续熟悉开发文档和k8s文档\n" +
"2.装电脑必备软件\n" +
"3.配置开发环境\n" +
"4.从GitLab上拉取项目并运行\n" +
"5.熟悉项目代码逻辑,就难理解地方进行标记");
summary.setProblem("文档中对有些接口和关键字不是很了解");
summary.display();
}
}
- 场景2:spring中scope=‘prototype’、ArrayList、HashMap用到了原型模式
在spring中当一个bean时,spring的ioc容器只会存在一个bean实例,当一个bean被标识为prototype时,每一次请求都会通过复制产生一个新实例
优势:
相当于将对象克隆一份,简化创建对象的过程
适用于需要大量对象,对象之间相同或相似,创建对象成本大如初始化时间长等情况,性能更高
弊端:
每一个类都需要写一个clone方法
对已有类进行改造的时候,需要修改代码,违背了开闭原则
2、模板模式
定义:封装了不变部分,扩展可变部分。它把认为是不变部分的公共算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展
- 场景1:实习生写实习总结这个例子也可以用模板模式来实现
代码
import java.util.Date;
//抽象类:工作总结
public abstract class SummaryTemplate {
//模板方法
final public void TemplateMethod() //模板方法
{
writeName(); //填写姓名
writeDepartment(); //填写部门
writeStatus(); //填写职位
}
//具体方法
public void writeName() {
System.out.print("姓名:张三");
}
public void writeDepartment() {
System.out.print("部门:研发三部");
}
public void writeStatus() {
System.out.print("职位:Java开发工程师");
}
//抽象方法
public abstract void writeDate(Date date);//填写日期
public abstract void writeInfo(String info); //填写工作总结
public abstract void writePlan(String plan); //填写工作计划
public abstract void writeProblem(String problem); //填写遇到的问题
}
import java.util.Date;
//具体子类
public class Trainee extends SummaryTemplate{
@Override
public void writeDate(Date date) {
System.out.println("日期:"+date);
}
@Override
public void writeInfo(String info) {
System.out.println("工作总结");
System.out.println(info);
}
@Override
public void writePlan(String plan) {
System.out.println("工作计划");
System.out.println(plan);
}
@Override
public void writeProblem(String problem) {
System.out.println("遇到的问题");
System.out.println(problem);
}
}
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
public class main {
public static void main(String[] args) {
SummaryTemplate st=new Trainee();
st.TemplateMethod();
st.writeDate(new Date());
st.writeInfo("1.学习k8s文档\\n\" +\n" +
"\"2.阅读桩桩项目代码及项目整体技术架构\\n\" +\n" +
"\"3.深入理解剩余11种设计模式并写demo");
st.writePlan("1.继续熟悉相关文档\\n\" +\n" +
"\"2.继续阅读桩桩项目代码及项目整体技术架构\\n\" +\n" +
"\"3.将设计模式应用到实际场景中,将多种设计模式混合使用并写demo\\n\" +\n" +
"\"4.完成组长布置的相应任务");
st.writeProblem("文档中对有些接口和关键字不是很了解");
}
}
- 场景2:spring中如AbstractPlatformTransactionManager是一个模板抽象类,他的具体子类有DataSourceTransactionManager、RabbitTransactionManager等
AbstractPlatformTransactionManager中的抽象方法
AbstractPlatformTransactionManager中的模板方法
DataSourceTransactionManager中的具体子类
优势:
在父类中提取了公共的部分代码,便于代码复用
子类可以通过扩展方式增加相应的功能,符合开闭原则
弊端:
每个不同的实现都需要定义一个子类,导致类的个数增加
如果父类中添加新的抽象方法,所有的子类都要加上这个方法
3、建造者模式
定义:指将一个复杂对象的构造与它的表示分离,分解为多个简单的对象,使同样的构建过程可以创建不同的表示
- 场景:实习生写实习总结这个例子也可以用建造者模式来实现,同样的构建过程可以创建不同的总结
代码
import java.util.Date;
public class Summary {
private String name;
private String department;
private String status;
private Date date;
private String info;
private String plan;
private String problem;
public Summary(String name, String departmant, String status, Date date, String info, String plan, String problem) {
this.name = name;
this.department = departmant;
this.status = status;
this.date = date;
this.info = info;
this.plan = plan;
this.problem = problem;
}
public void show(){
System.out.println("姓名:"+name + ",部门:"+department+",职位:"+status+",时间:"+date);
System.out.println("工作总结");
System.out.println(info);
System.out.println("工作计划");
System.out.println(plan);
System.out.println("遇到的问题");
System.out.println(problem);
}
}
import java.util.Date;
public class Builder {
private String name;
private String department;
private String status;
private Date date;
private String info;
private String plan;
private String problem;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder department(String department) {
this.department = department;
return this;
}
public Builder status(String status) {
this.status = status;
return this;
}
public Builder date(Date date) {
this.date = date;
return this;
}
public Builder info(String info) {
this.info = info;
return this;
}
public Builder plan(String plan) {
this.plan = plan;
return this;
}
public Builder problem(String problem) {
this.problem = problem;
return this;
}
public Summary build() {
return new Summary(this.name, this.department, this.status, this.date, this.info, this.plan, this.problem);
}
}
import java.util.Date;
public class main {
public static void main(String[] args) {
Builder builder=new Builder();
builder.name("张三")
.department("研发三部")
.status("Java开发工程师")
.date(new Date())
.info("1.学习k8s文档\n" +
"2.阅读桩桩项目代码及项目整体技术架构\n" +
"3.深入理解剩余11种设计模式并写demo")
.plan( "1.继续熟悉相关文档\n" +
"2.继续阅读桩桩项目代码及项目整体技术架构\n" +
"3.将设计模式应用到实际场景中,将多种设计模式混合使用并写demo\n" +
"4.完成组长布置的相应任务")
.problem("没有问题")
.build()
.show();
}
}
Builder可以用@Builder这个注解代替,封装了这个类中的方法
优势:
封装性好,构建和表示分离
扩展性好,各个具体的建造者相互独立,有利于系统的解耦
弊端:
如果具体对象发生变化,则建造者也要同步修改,用@Builder注解可以解决这个问题
与工厂模式的区别:
建造者模式注重零部件的组装过程
工厂方法模式注重零部件的创建过程
4、原型模式与模板方法模式和建造者模式的区别
原型模式是通过复制一个对象来创建一个模板,在模板上进行修改,比较灵活
模板方法模式是自定义一个模板,有些方法是固定写死的,只可修改由子类去扩展的模板方法
建造者模式侧重创建,而非修改,通过同样的创建过程创建不同的对象
5、责任链模式
定义:将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止
- 场景一:如果删除部门或角色,部门内的所有成员和等于该角色的成员也要被删除,可以采用责任链模式
代码
package com;
//抽象处理者:领导类
public abstract class Leader {
private Leader next;
public Leader getNext() {
return next;
}
public void setNext(Leader next) {
this.next = next;
}
public abstract void handlerRequest(String name);
}
package com;
//具体处理者:用户
class User extends Leader {
@Override
public void handlerRequest(String name) {
if("部门".equals(name)) {
System.out.println("删除部门内所有用户");
}else{
System.out.println("删除所有该角色的用户");
}
}
}
package com;
//具体处理者2:角色
class Role extends Leader {
@Override
public void handlerRequest(String name) {
System.out.println("删除角色");
getNext().handlerRequest(name);
}
}
package com;
//具体处理者3:部门
class Department extends Leader {
@Override
public void handlerRequest(String name) {
System.out.println("删除部门");
getNext().handlerRequest(name);
}
}
package com;
public class main {
public static void main(String[] args) {
Leader department=new Department();//部门
Leader role=new Role();//角色
Leader user=new User();//用户
department.setNext(user);
role.setNext(user);
department.handlerRequest("部门");
role.handlerRequest("角色");
}
}
- 场景二:如果有一个请假系统,不知道应该找谁请假,可以先找人事请假,一层层审批,可以用责任链模式来实现
代码
//抽象处理者:领导类
public abstract class Leader {
private Leader next;
public Leader getNext() {
return next;
}
public void setNext(Leader next) {
this.next = next;
}
public abstract void handlerRequest(int leaveDays);
}
//具体处理者1:人事
class HR extends Leader {
@Override
public void handlerRequest(int LeaveDays) {
if (LeaveDays <= 3) {
System.out.println("人事批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handlerRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
//具体处理者2:部门经理类
public class DepartmentHead extends Leader {
@Override
public void handlerRequest(int LeaveDays) {
if (LeaveDays <= 7) {
System.out.println("部门经理批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handlerRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
//具体处理者3:总经理类
class GeneralManager extends Leader {
@Override
public void handlerRequest(int LeaveDays) {
if (LeaveDays <= 30) {
System.out.println("总经理批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handlerRequest(LeaveDays);
} else {
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
public class main {
public static void main(String[] args) {
Leader Hr=new HR();//人事
Leader departmentHead=new DepartmentHead();//部门经理
Leader generalManager=new GeneralManager();//总经理
Hr.setNext(departmentHead);
departmentHead.setNext(generalManager);
Hr.handlerRequest(2);
Hr.handlerRequest(8);
}
}
优势:
发送者无需知道是哪个接收者处理请求以及链的结构,降低耦合度,可以根据需要增加新的请求处理类,增加可扩展性
简化了对象之间的连接,每个对象只需把持一个后引用,避免了众多if else
弊端:
不能保证每个请求一定能被处理
如果职责链太长,请求的处理涉及多个处理对象,影响性能,可能造成循环引用
6、代理模式
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。代理对象作为访问对象和目标对象之间的中介
- 场景一:如果要打印每个用户登陆注册的日志,可以使用代理模式
代码
package com;
//抽象主题角色
public interface User {
void login(String name);
void register(String name);
}
package com;
import java.util.Date;
//目标对象
public class LoginAndRegister implements User{
@Override
public void login(String name) {
System.out.println(name+"在"+new Date()+"登录");
}
@Override
public void register(String name) {
System.out.println(name+"在"+new Date()+"注册");
}
}
package com;
//代理类
public class StaticProxy implements User{
private LoginAndRegister loginAndRegister;
public StaticProxy(LoginAndRegister loginAndRegister) {
this.loginAndRegister = loginAndRegister;
}
@Override
public void login(String name) {
loginAndRegister.login(name);
}
@Override
public void register(String name) {
loginAndRegister.register(name);
}
}
package com;
public class main {
public static void main(String[] args) {
StaticProxy staticProxy =new StaticProxy(new LoginAndRegister());
staticProxy.login("张三");
staticProxy.register("李四");
}
}
代理模式可以扩展目标对象的功能,将客户端与目标对象分离,降低了系统的耦合度
但是目标对象与代理类一一对应,增加目标对象也要增加代理类,且涉及代理类之前,目标对象要先存在,这都是静态代理模式的弊端,动态代理可以很好地解决这个问题
动态代理代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//代理类
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = method.invoke(target, args);
return object;
}
}
import java.lang.reflect.Proxy;
public class main {
public static void main(String[] args) {
User loginAndRegist = new LoginAndRegister();
User proxy = (User) Proxy.newProxyInstance(
loginAndRegist.getClass().getClassLoader(), //loader 指的是目标对象对应的classLoader
loginAndRegist.getClass().getInterfaces(),//interfaces是设置为对象所实现的接口类型
new DynamicProxy(loginAndRegist));//实现了InvocationHandler接口的类对象
proxy.register("张三");
proxy.login("李四");
}
}
- 场景二:统计请求耗时、登录鉴定、事务、全局捕获异常和Spring AOP等都用到了代理模式
7、策略模式
定义:定义了一系列算法,通过对算法进行封装,使它们可以相互替换,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理
- 场景一:上面的请假功能中,如果用户知道请假天数和审批人的对应关系,可以直接去找审批人请假,可以通过策略模式来实现
代码
package com;
//抽象策略类
public interface MemberStrategy {
void operation(int days);
}
package com;
public class Context {
private MemberStrategy strategy;
public Context(MemberStrategy strategy) {
this.strategy=strategy;
}
public void executeStrategy(int days) {
strategy.operation(days);
}
}
package com;
//具体策略类:人事
public class HR implements MemberStrategy{
@Override
public void operation(int days) {
if(days<=3) {
System.out.println("人事给你批假" + days + "天");
}else{
System.out.println("无法批假");
}
}
}
package com;
//具体策略类:部门经理
public class Department implements MemberStrategy{
@Override
public void operation(int days) {
if(days>=3&&days<=7){
System.out.println("部门经理给你批假" + days + "天");
}else{
System.out.println("无法批假");
}
}
}
package com;
//具体策略类:总经理
public class GeneralManager implements MemberStrategy{
@Override
public void operation(int days) {
if(days>=7){
System.out.println("总经理给你批假" + days + "天");
}else{
System.out.println("无法批假");
}
}
}
package com;
public class main {
public static void main(String[] args) {
Context context=new Context(new Department());
context.executeStrategy(5);
}
}
- 场景二:DispatcherServlet处理请求需要依赖很多组件类,如处理器映射器、处理器适配器和视图解析器,通过策略模式,很容易更换其他组件,在DispatcherServlet.properties这个文件中配置了一些默认策略,例如视图解析器初始化时,springmvc会先从绑定的ApplicationContext中获取对应的视图解析器,如果没有就从默认策略中获取一个
优势:
避免多重条件语句,避免重复代码,提供相同行为的不同实现
可以在不修改原代码的情况下,灵活增加新算法
弊端:
客户端必须理解所有策略算法的区别,一遍选择适当的算法类
8、策略模式与责任链的区别
都能消除 if-else 分支过多的问题,策略模式的多种算法彼此之间是独立的,可自行更换策略算法,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责
9、装饰者模式
定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式
- 应用场景:如果要按照特定的多种规则对人员进行排序,可以使用装饰者模式,先根据是否在职排序,再根据性别排序,最后根据姓名排序
代码
package com;
public class User {
Boolean working;//是否在职
private String sex;//性别
private String name;//姓名
public User(String name,String sex, Boolean working) {
this.working = working;
this.sex = sex;
this.name = name;
}
public Boolean getWorking() {
return working;
}
public void setWorking(Boolean working) {
this.working = working;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"working=" + working +
", sex='" + sex + '\'' +
", name='" + name + '\'' +
'}';
}
}
package com;
import java.util.List;
//抽象构件角色
public interface Sorting {
//对list排序
void sortingSets(List<User> list);
}
package com;
import java.util.Comparator;
import java.util.List;
//具体构件(ConcreteComponent)角色
public class SortingByName implements Sorting{
@Override
public void sortingSets(List<User> list) {
//按照姓名排序
list.sort(Comparator.comparing(user->user.getName()));
}
}
package com;
import java.util.List;
//抽象装饰角色
public abstract class AbstractSorting implements Sorting{
//持有一个需要装饰的类
private Sorting sorting;
public AbstractSorting(Sorting sorting) {
super();
this.sorting = sorting;
}
@Override
public void sortingSets(List<User> list) {
//调用被装饰的类的方法
sorting.sortingSets(list);
}
}
package com;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
//具体装饰角色
public class SortingByWorking extends AbstractSorting{
public SortingByWorking(Sorting sorting) {
super(sorting);
}
@Override
public void sortingSets(List<User> list) {
//调用被装饰的类的排序方法
super.sortingSets(list);
//按照是否在职排序,在职排在离职之前
Collections.sort(list, new Comparator<User>(){
@Override
public int compare(User o1, User o2) {
Boolean work1=o1.getWorking();
Boolean work2=o2.getWorking();
if (work1&&work2) {
return 0;
} else {
return work1?-1:1;
}
}
});
}
}
package com;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
//具体装饰角色
public class SortingBySex extends AbstractSorting{
public SortingBySex(Sorting sorting) {
super(sorting);
}
@Override
public void sortingSets(List<User> list) {
//调用被装饰的类的排序方法
super.sortingSets(list);
//按照性别排序,女士优先
Collections.sort(list, new Comparator<User>(){
@Override
public int compare(User o1, User o2) {
String sex1=o1.getSex();
String sex2=o2.getSex();
if (sex1.equals(sex2)) {
return 0;
} else {
return sex1.equals("男")?1:-1;
}
}
});
}
}
package com;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
public class main {
public static void main(String[] args) {
List<User> list = new ArrayList<User>();
list.add(new User("zhangsan", "男",true));
list.add(new User("lisi", "女",false));
list.add(new User("ww", "女",true));
list.add(new User("xiaoxi", "男",false));
list.add(new User("laowang", "男",false));
list.add(new User("z1", "女",true));
list.add(new User("z2", "女",false));
list.add(new User("z3", "女",true));
list.add(new User("mayun", "男",false));
//排序
Sorting sort=new SortingByWorking(new SortingBySex(new SortingByName()));
sort.sortingSets(list);
//先根据是否在职排序,再调用根据性别排序,最后调用根据姓名排序
list.forEach(user->System.out.println(user));
}
}
- 场景二:
优势:
比继承灵活,在不改变原有对象的情况下,动态地给一个对象拓展功能
即插即用,完全遵守开闭原则
弊端:
会增加很多子类,过度使用会增加程序得复杂性
10、组合模式
定义:是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性
- 应用场景:如果在一个企业里要统计每个组,每个部门的人数,可以采用组合模式去实现这个功能,显示某组或某部门的人员和人数
代码
package com;
//抽象构件
public interface Member {
int calculation();
void show();
}
package com;
//树叶构件
public class User implements Member {
private String name;
public User(String name) {
this.name = name;
}
@Override
public int calculation() {
return 1;
}
@Override
public void show() {
System.out.println(name);
}
}
package com;
import java.util.ArrayList;
import java.util.List;
//树枝构件
public class Factory implements Member {
private List<Member> children=new ArrayList<>();
public void add(Member member) {
children.add(member);
}
@Override
public int calculation() {
int count=0;
for(Member member :children){
count+= member.calculation();
}
return count;
}
@Override
public void show() {
for(Member member :children){
member.show();
}
}
}
package com;
public class main {
public static void main(String[] args) {
Factory group1=new Factory();//1组
Factory group2=new Factory();//2组
Factory department1=new Factory();//1部门
Factory department2=new Factory();//2部门
Factory factory=new Factory();//公司
group1.add(new User("张三"));
group1.add(new User("李四"));
group1.add(new User("王五"));
group2.add(new User("孙悟空"));
group2.add(new User("猪八戒"));
group2.add(new User("沙和尚"));
department1.add(new User("老王"));//1部门经理
department2.add(new User("小刘"));//2部门经理
department1.add(group1);
department1.add(group2);
factory.add(new User("马云"));//总经理
factory.add(department1);
factory.add(department2);
System.out.println("公司总人数为:"+factory.calculation()+" ,公司人员为:");
factory.show();
System.out.println("1部门人数为: "+department1.calculation()+" ,1部门人员为");
department1.show();
}
}
优势:
可以一致地处理单个对象和组合对象
方便在组合体中加入新的对象,不用修改原代码,满足开闭原则
弊端:
客户端需要花更多时间理清类之间的层次关系
不容易用继承的方法来增加构件的新功能
11、命令模式+备忘录模式
命令模式定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通
备忘录模式定义:在不破坏封装性的前提下,捕获一个对象的内部状态并保存,以便以后当需要时能将该对象恢复到原先保存的状态
- 应用场景:假如设计一个打卡系统,如果9点上班,九点半误操作,更新打卡了一次,就显示上班时间是九点半了,要实现撤销打卡功能,可以采用备忘录模式,可用命令模式辅助实现
代码
//抽象命令
public interface AbstractCommand {
void execute();
}
import java.util.Date;
//树叶构件: 具体命令(切换)
public class actionPerformed implements AbstractCommand {
private Date date;
private CompositeReceiver receiver;
actionPerformed(Date date) {
this.date=date;
receiver = new CompositeReceiver();
}
@Override
public void execute() {
receiver.action1(date);
}
}
//树叶构件: 具体命令(撤销)
public class returnPerformed implements AbstractCommand {
private CompositeReceiver receiver;
returnPerformed() {
receiver = new CompositeReceiver();
}
@Override
public void execute() {
receiver.action2();
}
}
import java.util.ArrayList;
//树枝构件: 调用者
public class CompositeInvoker implements AbstractCommand {
//与组合模式结合,将多个命令装配成一个组合命令->宏命令
private ArrayList<AbstractCommand> children = new ArrayList<>();
public void add(AbstractCommand c) {
children.add(c);
}
@Override
public void execute() {
for (Object obj : children) {
((AbstractCommand) obj).execute();
}
}
}
import java.util.ArrayList;
import java.util.Date;
//接收者
public class CompositeReceiver {
public void action1(Date date){
boolean ok = main.workers.push(main.you.createMemento()); //保存状态
if (ok) {
main.you.setDate(date);
System.out.println("你在"+date+"打卡成功");
}
}
public void action2() {
main.you.restoreMemento(main.workers.pop()); //恢复状态
System.out.println("最后打卡时间恢复为"+main.you.getDate());
}
}
import java.util.Date;
public class Worker {
private Date date;
public Worker(Date date) {
this.date = date;
}
public void setDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
//员工栈
public class Workertack {
private Worker worker[];
private int top;
Workertack() {
worker = new Worker[5];
top = -1;
}
public boolean push(Worker p) {
if (top >= 4) {
System.out.println("你太粗心了,变来变去的!");
return false;
} else {
worker[++top] = p;
return true;
}
}
public Worker pop() {
if (top <= 0) {
System.out.println("当前只有一次打卡了哦!");
return worker[0];
} else {
return worker[top--];
}
}
}
import java.util.Date;
public class You {
private Date date; //打卡时间
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Worker createMemento() {
return new Worker(date);
}
public void restoreMemento(Worker p) {
setDate(p.getDate());
}
}
import java.util.Date;
public class main {
static You you=null;
static Workertack workers=null;
public static void main(String[] args) throws InterruptedException {
you=new You();
workers=new Workertack();
workers.push(you.createMemento());//保存状态
CompositeInvoker ir = new CompositeInvoker();
ir.add(new actionPerformed(new Date()));
Thread.sleep(1000);
ir.add(new actionPerformed(new Date()));
Thread.sleep(1000);
ir.add(new actionPerformed(new Date()));
Thread.sleep(1000);
ir.add(new actionPerformed(new Date()));
Thread.sleep(1000);
ir.add(new actionPerformed(new Date()));
//撤销打卡
ir.add(new returnPerformed());
ir.execute();
}
}
命令模式优势:
通过组件降低耦合度
扩展性好,可以与组合模式结合,将多个命令装配成一个组合命令
与备忘录模式结合,实现命令的撤销与恢复
可以在现有命令的基础上,增加额外功能,与装饰器模式结合
弊端:可能产生大量的命令类
备忘录模式优势:
提供了一种恢复状态的机制
简化了发起类,所有状态信息都保存在备忘录中,由管理者同一管理
符合单一职责原则
弊端:
自愿消耗大,保存的内部状态过多或者特别频繁,会占用内存资源
五、结束语
很荣幸能和大家交流常用设计模式在实际开发中的应用,设计模式可以提高我们开发工程师的思维能力、编程能力和设计能力,也可以使程序设计更加标准化,提高软件开发效率,缩短软件开发周期。
希望能帮助到大家!