JAVA设计模式(篇九)
温馨提示:
本文最后更新于 2022年10月27日,已超过 918 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我。
十五-命令模式
在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系。这不利于软件功能的扩展与维护。例如,想对行为进行“撤销、重做、记录"等处理都很不方便,因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。
1.命令模式的定义与特点
1. 命令模式定义:将一个请求封装为一个对象(具体命令),使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
2. 优点:
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
- 方便实现Undo和Redo操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
3. 缺点:
- 会产生大量具体命令类。因为对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
2. 模式的结构和实现
命令模式由抽象命令角色,具体命令角色,接收者(实现者),调用者(请求者) 组成
1. 模式的结构
- 抽象命令角色: 声明一个抽象命令接口,包含执行命令方法。
- 具体命令角色: 实现抽象命令接口,包含一个接收者(实现者)对象,调用接受者对应的功能完成命令需要执行的操作。
- 接收者(实现者):执行命令相关操作,是命令的真正实现者。
- 调用者(请求者):请求的发送者,通常拥有多个具体命令对象,通过调用具体命令实现相应的功能,不直接访问接收者(实现解耦);
结构图:
2.模式的实现
//抽象命令,拥有执行方法
//模拟用户到餐馆就餐,用户可以选择吃川菜,可以吃粤菜,可以吃干锅等等。
//用户为抽象命令,前台服务员为方法调用者,厨师相当于接收者
public interface AbstractOrder {
void execute();
}
//接收者==川菜师傅
public class SichuanCuisineChef {
public void execute(){
System.out.println("川菜师傅烹饪川菜。。。。");
}
}
//接收者==粤菜师傅
public class YueCuisineChef {
public void execute(){
System.out.println("粤菜师傅烹饪粤菜。。。。");
}
}
//具体命令==吃川菜的命令
public class SichuanCuisineOrder implements AbstractOrder{
private SichuanCuisineChef cuisineChef;
public SichuanCuisineOrder(){
this.cuisineChef = new SichuanCuisineChef();
}
@Override
public void execute() {
this.cuisineChef.execute();
}
}
//具体命令==吃粤菜的命令
public class YueCuisineOrder implements AbstractOrder{
private YueCuisineChef cuisineChef;
public YueCuisineOrder(){
this.cuisineChef = new YueCuisineChef();
}
@Override
public void execute() {
this.cuisineChef.execute();
}
}
//调用者==服务员
public class InvokerWaiter {
private AbstractOrder order;
public void setOrder(AbstractOrder order){
this.order = order;
}
public void cook(){
this.order.execute();
}
}
//测试 用户想吃川菜
@Test
public void order(){
InvokerWaiter invokerWaiter = new InvokerWaiter();
invokerWaiter.setOrder(new SichuanCuisineOrder());
invokerWaiter.cook();
}
川菜师傅烹饪川菜。。。。
3.模式的应用场景
- 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
- 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
- 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
- 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。
4.命令模式的扩展
上述用户到餐馆点餐时,可能不止会点川菜,还会点粤菜等等,此时就需要宏命令来实现 。
将命令模式和组合模式进行组合,便构成了组合命令即宏命令
//抽象命令,拥有执行方法
//模拟用户到餐馆就餐,用户可以选择吃川菜,可以吃粤菜,可以吃干锅等等。
//用户为抽象命令,前台服务员为方法调用者,厨师相当于接收者
public interface AbstractOrder {
void execute();
}
//接收者==川菜师傅
public class SichuanCuisineChef {
public void execute(){
System.out.println("川菜师傅烹饪川菜。。。。");
}
}
//接收者==粤菜师傅
public class YueCuisineChef {
public void execute(){
System.out.println("粤菜师傅烹饪粤菜。。。。");
}
}
//具体命令==吃川菜的命令 ==树叶构件
public class SichuanCuisineOrder implements AbstractOrder{
private SichuanCuisineChef cuisineChef;
public SichuanCuisineOrder(){
this.cuisineChef = new SichuanCuisineChef();
}
@Override
public void execute() {
this.cuisineChef.execute();
}
}
//具体命令==吃粤菜的命令 ==树叶构件
public class YueCuisineOrder implements AbstractOrder{
private YueCuisineChef cuisineChef;
public YueCuisineOrder(){
this.cuisineChef = new YueCuisineChef();
}
@Override
public void execute() {
this.cuisineChef.execute();
}
}
//组合命令调用者==服务员 == 树枝构件
public class CombinationInvoker {
private List<AbstractOrder> child = new ArrayList<>();
public void addOrder(AbstractOrder order){
this.child.add(order);
}
public void removeOrder(AbstractOrder order){
this.child.remove(order);
}
//递归执行
public void execute(){
if (!CollectionUtils.isEmpty(this.child)){
this.child.forEach(c->c.execute());
}
}
}
//测试 用户想吃川菜+粤菜
@Test
public void order(){
CombinationInvoker invoker = new CombinationInvoker();
invoker.addOrder(new SichuanCuisineOrder());
invoker.addOrder(new YueCuisineOrder());
invoker.execute();
}
川菜师傅烹饪川菜。。。。
粤菜师傅烹饪粤菜。。。。
十六-责任链模式
一个请求有多个对象可以处理,但每个对象的处理条件或权限不同,就需要找到不同的请求处理者。比如公司的款项报销,低于500元的,财务可以直接审批,高于500元小于2000元的,需要主管进行审批,高于2000元的,则需要总经理审批。。
1.模式的定义与特点
责任链模式:为了避免请求发送者和多个请求处理者耦合在一起,所以在每个请求处理者中都包含下一个请求处理者的引用,这样就形成了一个请求处理链。当请求发起时,就可以顺着责任链找到对应的处理者,直到处理为止。
责任链模式又称之为职责链模式
优点:
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if…else语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点:
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
2.模式的结构和实现
责任链模式是包含抽象处理者,具体处理者,客户类角色。
1.责任链模式的结构
- 抽象处理者: 定义一个请求处理的接口,包含请求处理的方法和后续处理者。
- 具体处理者: 实现抽象处理者,实际请求的处理者,判断该请求能否处理,如果不能处理,则交给后续处理者。
- 客户类角色: 创建一个责任链,并向责任链的第一个处理者发起请求,本身不关系处理的细节和传递过程。
2.责任链模式的实现
以财务报销为例,低于500元的,财务可以直接审批,高于500元小于2000元的,需要主管进行审批,高于2000元的,则需要总经理审批。。
//抽象报销者
public abstract class ReimbursementHandler {
private ReimbursementHandler reimbursementHandler;
public ReimbursementHandler getReimbursementHandler() {
return reimbursementHandler;
}
public void setReimbursementHandler(ReimbursementHandler reimbursementHandler) {
this.reimbursementHandler = reimbursementHandler;
}
//抽象报销方法
public abstract void reimbursement(BigDecimal money);
}
//具体处理者==财务
public class FinanceHandler extends ReimbursementHandler{
@Override
public void reimbursement(BigDecimal money) {
if (money.compareTo(new BigDecimal(500))<1){
System.out.println("财务将该笔款项报销了,报销金额:"+money);
}else {
ReimbursementHandler reimbursementHandler = getReimbursementHandler();
if (null!=reimbursementHandler){
reimbursementHandler.reimbursement(money);
}else {
System.out.println("报销金额太高,无法进行报销");
}
}
}
}
//具体处理者==主管
public class MaterManagerHandler extends ReimbursementHandler{
@Override
public void reimbursement(BigDecimal money) {
if (money.compareTo(new BigDecimal(2000))<1){
System.out.println("主管将该笔款项报销了,报销金额:"+money);
}else {
ReimbursementHandler reimbursementHandler = getReimbursementHandler();
if (null!=reimbursementHandler){
reimbursementHandler.reimbursement(money);
}else {
System.out.println("报销金额太高,无法进行报销");
}
}
}
}
//具体处理者==总经理
public class GeneralManagerHandler extends ReimbursementHandler{
@Override
public void reimbursement(BigDecimal money) {
if (money.compareTo(new BigDecimal(5000))<1){
System.out.println("总经理将该笔款项报销了,报销金额:"+money);
}else {
ReimbursementHandler reimbursementHandler = getReimbursementHandler();
if (null!=reimbursementHandler){
reimbursementHandler.reimbursement(money);
}else {
System.out.println("报销金额太高,无法进行报销");
}
}
}
}
//测试,不同阶段的报销金额,可由不同职位的人进行报销
@Test
public void linkTest(){
ReimbursementHandler financeHandler = new FinanceHandler();
ReimbursementHandler materManagerHandler = new MaterManagerHandler();
ReimbursementHandler generalManagerHandler = new GeneralManagerHandler();
financeHandler.setReimbursementHandler(materManagerHandler);
materManagerHandler.setReimbursementHandler(generalManagerHandler);
financeHandler.reimbursement(new BigDecimal(6000));
}
3. 责任链模式的应用场景
- 有多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
- 可动态指定一组对象处理请求,或添加新的处理者。
- 在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
4.责任链模式的扩展
- 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任)﹔把责任推给下家处理。
- 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。
正文到此结束
- 本文标签: 设计模式
- 本文链接: https://www.tianyajuanke.top/article/58
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权