行为型模式

 

行为型模式

概念:

行为型模式主要涉及算法和对象间的职责分配。通过使用对象组合,行为型模式可以描述一组对象应该如何协作来完成一个整体任务。

所涵盖的模式有:

  1. 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
  2. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
  3. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  4. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
  5. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
  6. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
  7. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
  8. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
  9. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
  10. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
  11. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

   注:

    模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式

责任链模式

责任链(Chain of Responsibility)模式的定义:

为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

职责链模式主要包含以下角色。

抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。

 

利用责任链模式实现员工请假

下面是代码:

 

/**
 * 请假对象
 * @author Administrator
 *
 */
public class LeaveRequest {
    //请假人
    private String leaveName;
    //请假天数
    private int leaveDay;
    //请假原因
    private String leaveReasen;
    public LeaveRequest(String leaveName, int leaveDay, String leaveReasen) {
        super();
        this.leaveName = leaveName;
        this.leaveDay = leaveDay;
        this.leaveReasen = leaveReasen;
    }
    public String getLeaveName() {
        return leaveName;
    }
    public int getLeaveDay() {
        return leaveDay;
    }
    public String getLeaveReasen() {
        return leaveReasen;
    }   
}
//抽象处理者
public abstract class Leader {
    protected String nameString;

    protected Leader nextLeader;

    public void setNextLeader(Leader nextLeader) {
        this.nextLeader = nextLeader;
    }

    public Leader(String nameString) {
        this.nameString = nameString;
    }

    //处理请求的方法
    public abstract void LeaderRequest(LeaveRequest leaveRequest);
}
/**
 * 具体处理者1
 * 项目经理,处理请假天数小于等于三天的员工
 * @author Administrator
 *
 */
public class ProjectManager extends Leader{

    public ProjectManager(String nameString) {
        super(nameString);
    }

    @Override
    public void LeaderRequest(LeaveRequest leaveRequest) {
        if(leaveRequest.getLeaveDay()<=3) {
            System.out.println("员工:"+leaveRequest.getLeaveName()
            +" 请假天数:"+leaveRequest.getLeaveDay()
            +" 请假原因:"+leaveRequest.getLeaveReasen()
            +"已批准!");
            System.out.println(nameString+"批准");
        }else {
            if(this.nextLeader!=null) {
                this.nextLeader.LeaderRequest(leaveRequest);
            }
        }
    }
   
}
/**
 * 具体处理者2
 * 副经理,处理请假天数大于3天,小于等于10十天的员工
 * @author Administrator
 *
 */
public class AssistantManager extends Leader {

    public AssistantManager(String nameString) {
        super(nameString);
    }
   
    @Override
    public void LeaderRequest(LeaveRequest leaveRequest) {
        if(leaveRequest.getLeaveDay()<=10) {
            System.out.println("员工:"+leaveRequest.getLeaveName()
                               +" 请假天数:"+leaveRequest.getLeaveDay()
                               +" 请假原因:"+leaveRequest.getLeaveReasen()
                               +"已批准!");
            System.out.println(nameString+"批准");
        }else {
            if(this.nextLeader!=null) {
                this.nextLeader.LeaderRequest(leaveRequest);
            }
        }
    }
}
/**
 * 具体处理者3
 * 总经理,处理请假天数大于7天,小于15天的员工
 * @author Administrator
 *
 */
public class GeneralManager extends Leader {

    public GeneralManager(String nameString) {
        super(nameString);
    }
    @Override
    public void LeaderRequest(LeaveRequest leaveRequest) {
        if(leaveRequest.getLeaveDay()<=15) {
            System.out.println("员工:"+leaveRequest.getLeaveName()
                               +" 请假天数:"+leaveRequest.getLeaveDay()
                               +" 请假原因:"+leaveRequest.getLeaveReasen()
                               +"已批准!");
            System.out.println(nameString+"批准");
        }else {
        
                System.out.println("请假天数过于多,不予批准!");
            
        }
    }
}
public class Client {
   public static void main(String[] args) {
     LeaveRequest leaveRequest=new LeaveRequest("TOM",14, "回家");
     
     Leader a=new ProjectManager("项目经理");
     Leader b=new AssistantManager("副经理");
     Leader c=new GeneralManager("总经理");
     
     //创建责任链
     a.setNextLeader(b);
     b.setNextLeader(c);
     
     a.LeaderRequest(leaveRequest);
      }
}

 

结果: 

员工:TOM 请假天数:14 请假原因:回家已批准!
总经理批准

 

迭代器模式

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。


    既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

1暴露了聚合类的内部表示,使其数据不安全;

2增加了客户的负担。

“迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”,如 Java 中的 Collection、List、Set、Map 等都包含了迭代器。

具体定义:

提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

迭代器模式主要包含以下角色。

  1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
  4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

 

代码:

/**
 * 聚合类接口
 * 
 * @author Administrator
 *
 */
public interface Aggregate {
    public void add(Object obj);

    public void remove(Object obj);

    public MyIterator getIterator();
}

/**
 * 迭代器接口
 * 
 * @author Administrator
 *
 */
public interface MyIterator {

    Object first();

    Object next();

    boolean hasNext();
}

/**
 * 具体的聚合类 具体的迭代器我们利用内部类在聚合内内部构造,这样迭代器类可以访问聚合类的所有属性与方法
 * 
 * @author Administrator
 *
 */
public class ConcreteAggregate implements Aggregate {
    private List<Object> list = new ArrayList<Object>();

    @Override
    public void add(Object obj) {
        list.add(obj);
    }

    @Override
    public void remove(Object obj) {
        list.remove(obj);

    }

    @Override
    public MyIterator getIterator() {
        return (new ConcreateIterator());
    }

    // 内部类
    private class ConcreateIterator implements MyIterator {

        private int index = -1;

        @Override
        public Object first() {
            index = 0;
            return list.get(index);
        }

        @Override
        public Object next() {
            Object object = null;
            if (this.hasNext()) {// 返回下一个元素
                object = list.get(++index);
            }
            return object;
        }

        @Override
        public boolean hasNext() {
            if (index < list.size() - 1) {
                return true;
            } else {
                return false;
            }
        }

    }
}

public class Client {
    public static void main(String[] args) {
        Aggregate ag = new ConcreteAggregate();
        ag.add(1);
        ag.add(2);
        ag.add(3);
        System.out.print("聚合的内容有:\n");
        MyIterator iterator = ag.getIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

 

结果:

聚合的内容有:
1
2
3

中介者模式

   现实生活中。倘若一个公司的研发部,财务部,市场部是三个独立的部门,那么公司运作中,这三个部门之间可以随意交互,这种复杂的交互关系必然导致公司杂乱不堪,所以此时需要有一个中介者(总经理)所有部门需要做什么都和总经理说明,有总经理来进行分配,这样极大的减少了公司复杂关系的冗余

定义

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

中介者模式包含以下主要角色。

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  2. 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

 

代码:

 

public class MediatorPattern {

     public static void main(String[] args) {
            Mediator md = new ConcreteMediator();
            Colleague c1, c2;
            c1 = new ConcreteColleague1();
            c2 = new ConcreteColleague2();
            md.register(c1);
            md.register(c2);
            c1.send();
            System.out.println("-------------");
            c2.send();
        }
    }

    //抽象中介者
    abstract class Mediator {
        public abstract void register(Colleague colleague);//注册

        public abstract void relay(Colleague cl); //转发
    }

    //具体中介者
    class ConcreteMediator extends Mediator {
        private List<Colleague> colleagues = new ArrayList<Colleague>();//中介者持有的同事类对象

        public void register(Colleague colleague) {
            if (!colleagues.contains(colleague)) {
                colleagues.add(colleague);
                colleague.setMedium(this);
            }
        }

        public void relay(Colleague cl) {
            for (Colleague ob : colleagues) {
                if (!ob.equals(cl)) {
                    ((Colleague) ob).receive();
                }
            }
        }
    }

    //抽象同事类
    abstract class Colleague {
        protected Mediator mediator;

        public void setMedium(Mediator mediator) {
            this.mediator = mediator;
        }

        public abstract void receive();

        public abstract void send();
    }

    //具体同事类
    class ConcreteColleague1 extends Colleague {
        public void receive() {
            System.out.println("具体同事类1收到请求。");
        }

        public void send() {
            System.out.println("具体同事类1发出请求。");
            mediator.relay(this); //请中介者转发
        }
    }

    //具体同事类
    class ConcreteColleague2 extends Colleague {
        public void receive() {
            System.out.println("具体同事类2收到请求。");
        }

        public void send() {
            System.out.println("具体同事类2发出请求。");
            mediator.relay(this); //请中介者转发
        }
}

 

结果:

具体同事类1发出请求。
具体同事类2收到请求。
-------------
具体同事类2发出请求。
具体同事类1收到请求。

命令模式

 

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。、

将请求封装,这样的目的是为了避免请求调用者和请求接收者直接交互,而是在中间加入一个命令对象,来减少程序之间的耦合

命令模式包含以下主要角色。

  1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  2. 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

 

  代码:

 

public class CommandPattern {
    public static void main(String[] args) {
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客户访问调用者的call()方法...");
        ir.call();
    }
}

//调用者
class Invoker {
    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    public void call() {
        System.out.println("调用者执行命令command...");
        command.execute();
    }
}

//抽象命令
interface Command {
    public abstract void execute();
}

//具体命令
class ConcreteCommand implements Command {
    private Receiver receiver;

    ConcreteCommand() {
        receiver = new Receiver();
    }

    public void execute() {
        receiver.action();
    }
}

//接收者
class Receiver {
    public void action() {
        System.out.println("接收者的action()方法被调用...");
    }
}

结果:

客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...

解释器模式

在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样的情况可以使用解释器模式,该模式的定义为:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

由于应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

 

访问者模式

 

使用场景:

 

 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作

 

 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。

 

定义:

 

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

访问者模式包含以下主要角色。

  1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
  2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
  4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
  5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

 

代码:

public class VisitorPattern {
    public static void main(String[] args) {
        ObjectStructure os = new ObjectStructure();
        os.add(new ConcreteElementA());
        os.add(new ConcreteElementB());
        Visitor visitor = new ConcreteVisitorA();
        os.accept(visitor);
        System.out.println("------------------------");
        visitor = new ConcreteVisitorB();
        os.accept(visitor);
    }
}
//抽象访问者
interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}
//具体访问者A类
class ConcreteVisitorA implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具体访问者A访问-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具体访问者A访问-->" + element.operationB());
    }
}
//具体访问者B类
class ConcreteVisitorB implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("具体访问者B访问-->" + element.operationA());
    }
    public void visit(ConcreteElementB element) {
        System.out.println("具体访问者B访问-->" + element.operationB());
    }
}
//抽象元素类
interface Element {
    void accept(Visitor visitor);
}
//具体元素A类
class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationA() {
        return "具体元素A的操作。";
    }
}
//具体元素B类
class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    public String operationB() {
        return "具体元素B的操作。";
    }
}
//对象结构角色
class ObjectStructure {
    private List<Element> list = new ArrayList<Element>();
    public void accept(Visitor visitor) {
        Iterator<Element> i = list.iterator();
        while (i.hasNext()) {
            ((Element) i.next()).accept(visitor);
        }
    }
    public void add(Element element) {
        list.add(element);
    }
    public void remove(Element element) {
        list.remove(element);
    }
}

 

结果:

具体访问者A访问-->具体元素A的操作。
具体访问者A访问-->具体元素B的操作。
------------------------
具体访问者B访问-->具体元素A的操作。
具体访问者B访问-->具体元素B的操作。

 



访问者模式的缺点也很明显:
  1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

 

 

策略模式

假设有一个程序其代码的目的呢是实现根据用户的购买量来进行打折这个问题,普通的代码我们可能会想到,用多重条件转移语句实现,不但是条件语句变得很复杂,而且增加,删除或者更换算法都要修改源代码,违反了开闭原则。此时我们需要使用策略模式

定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

主要角色:

  1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  3. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

 

代码:

 

public class StrategyPattern {
    public static void main(String[] args) {
         Context c = new Context();
//使用策略A Strategy s
= new ConcreteStrategyA(); c.setStrategy(s); c.strategyMethod(); System.out.println("-----------------");
//使用策略B s
= new ConcreteStrategyB(); c.setStrategy(s); c.strategyMethod(); } } //抽象策略类 interface Strategy { public void strategyMethod(); //策略方法 } //具体策略类A class ConcreteStrategyA implements Strategy { public void strategyMethod() { System.out.println("具体策略A的策略方法被访问!"); } } //具体策略类B class ConcreteStrategyB implements Strategy { public void strategyMethod() { System.out.println("具体策略B的策略方法被访问!"); } } //环境类 class Context { private Strategy strategy; public Strategy getStrategy() { return strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void strategyMethod() { strategy.strategyMethod(); } }

 

结果:

具体策略A的策略方法被访问!
-----------------
具体策略B的策略方法被访问!

 

模板方法模式

 

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

主要角色:

1)抽象类/抽象模板(Abstract Class)

抽象模板类,负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。
① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
② 基本方法:是整个算法中的一个步骤,包含以下几种类型。

  • 抽象方法:在抽象类中声明,由具体子类实现。
  • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
  • 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

2)具体子类/具体实现(Concrete Class)

具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。

Demo:模拟一个餐馆吃饭的流程,假设固定不变的是看菜谱,吃饭,结账,中间吃的什么菜我们还不知道,此时我们可以使用模板方法模式,根据自己的需求去点餐

代码:

 

public class TemplateMethodPattern {
    public static void main(String[] args) {
        AbstractClass ac = new Fish();
        ac.TemplateMethod();
        System.out.println("-------");
        AbstractClass ac2 = new Beef();
        ac.TemplateMethod();
        System.out.println("-------");
        // 也可以使用匿名内部类的方式
        AbstractClass ac3 = new AbstractClass() {

            @Override
            public void order() {
                System.out.println("番茄鸡蛋");
            }

        };
        ac3.TemplateMethod();
    }
}

//抽象类
abstract class AbstractClass {
    // 模板方法
    public void TemplateMethod() {
        lookAtMenu();
        order();
        eat();
        checkOut();
    }

    // 看菜谱
    public void lookAtMenu() {
        System.out.println("查看菜谱");
    }

    // 点餐
    public abstract void order();

    // 吃饭
    public void eat() {
        System.out.println("正在用餐");
    }

    // 结账
    public void checkOut() {
        System.out.println("用餐完毕,结账!");
    }
}

//具体子类
class Fish extends AbstractClass {

    @Override
    public void order() {
        System.out.println("来一道鱼肉");
    }
}

class Beef extends AbstractClass {

    @Override
    public void order() {
        // TODO Auto-generated method stub
        System.out.println("来一道牛肉");
    }

}

 

结果:

  

查看菜谱
来一道鱼肉
正在用餐
用餐完毕,结账!
-------
查看菜谱
来一道鱼肉
正在用餐
用餐完毕,结账!
-------
查看菜谱
番茄鸡蛋
正在用餐
用餐完毕,结账!

状态模式

应用程序中的部分对象可能会根据不同的情况做出不同的行为,这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为也发生改变.

对这种有状态的对象编程,传统的解决方案是将这些所有可能发生的情况全都考虑到,然后使用 if-else 或 switch-case 语句来做状态判断,再进行不同情况的处理。但是显然这种做法对复杂的状态判断存在天然弊端,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力。

主要角色:

            环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。

            抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

            具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

代码:

 

public class StatePattern{
    public static void main(String[] args) {
        Context context = new Context();    //创建环境      
        context.Handle();    //处理请求
        System.out.println("----------");
        context.setState(new ConcreteStateB());//修改状态为B
        context.Handle();
    }
}
//环境类
class Context {
    private State state;
    //定义环境类的初始状态
    public Context() {
        this.state = new ConcreteStateA();//默认状态是A
    }
    //设置新状态
    public void setState(State state) {
        this.state = state;
    }
    //读取状态
    public State getState() {
        return (state);
    }
    //对请求做处理
    public void Handle() {
        state.Handle(this);
    }
}
//抽象状态类
abstract class State {
    public abstract void Handle(Context context);
}
//具体状态A类
class ConcreteStateA extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 A");
    }
}
//具体状态B类
class ConcreteStateB extends State {
    public void Handle(Context context) {
        System.out.println("当前状态是 B");
    }
}

 

结果:

当前状态是 A
----------
当前状态是 B

优点

1.结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。

2.将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。

3.状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。


缺点:

1.状态模式的使用必然会增加系统的类与对象的个数。

2.状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

33.状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

 

 

观察者模式

定义:

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

注意:

   实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

主要角色:

     抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。

     具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。

     抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。

     具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

 

代码:

 

public class ObserverPattern {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer obs1 = new ConcreteObserver1();
        Observer obs2 = new ConcreteObserver2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
    }
}

//抽象目标
abstract class Subject {
    protected List<Observer> observers = new ArrayList<Observer>();

    //增加观察者方法
    public void add(Observer observer) {
        observers.add(observer);
    }

    //删除观察者方法
    public void remove(Observer observer) {
        observers.remove(observer);
    }

    public abstract void notifyObserver(); //通知观察者方法
}

//具体目标
class ConcreteSubject extends Subject {
    
    public void notifyObserver() {
        System.out.println("具体目标发生改变...");
        System.out.println("--------------");

        for (Object obs : observers) {
            ((Observer) obs).response();
        }

    }
}

//抽象观察者
interface Observer {
    void response(); //反应
}

//具体观察者1
class ConcreteObserver1 implements Observer {
    public void response() {
        System.out.println("具体观察者1作出反应!");
    }
}

//具体观察者1
class ConcreteObserver2 implements Observer {
    public void response() {
        System.out.println("具体观察者2作出反应!");
    }
}

 

结果:

 

 

 

具体目标发生改变...
--------------
具体观察者1作出反应!
具体观察者2作出反应!

 

备忘录模式

定义:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。

角色:

发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。

备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。

管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

代码:

    

public class MementoPattern {
    public static void main(String[] args) {
        Originator or = new Originator();
        Caretaker cr = new Caretaker();
        or.setState("S0");
        System.out.println("初始状态:" + or.getState());
        //保存状态
        cr.setMemento(or.createMemento());
        //修改状态
        or.setState("S1");
        System.out.println("新的状态:" + or.getState());
        //恢复状态
        or.restoreMemento(cr.getMemento());
        System.out.println("恢复状态:" + or.getState());
    }
}

//备忘录,备忘录存储的是发起人类的状态
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

//发起人:发起人类里除了有自己独有的属性和方法外。还需要有创建备忘录和恢复备忘录的方法
class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
    //创建备忘录
    public Memento createMemento() {
        return new Memento(state);
    }
    //恢复备忘录
    public void restoreMemento(Memento m) {
        this.setState(m.getState());
    }
}

//管理者
class Caretaker {
    private Memento memento;

    public void setMemento(Memento m) {
        memento = m;
    }

    public Memento getMemento() {
        return memento;
    }
}

 

结果:

初始状态:S0
新的状态:S1
恢复状态:S0

 

 

 

 

 

Demo:模拟一个餐馆吃饭的流程,假设固定不变的是看菜谱,吃饭,结账,中间吃的什么菜我们还不知道,此时我们可以使用模板方法模式,根据自己的需求去点餐

posted @ 2021-02-26 11:17  19A5  阅读(187)  评论(0编辑  收藏  举报