【行为型模式】《大话设计模式》——读后感 (10)无尽加班何时休?——状态模式

原文定义:

   状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变其类【DP】

UML结构图:

  

背景:

    看到此模式,酝酿了好久才决定对状态模式进行总结。光看原文定义,实在没有获取到什么有用的价值。

       第一眼看到状态模式,感觉这不就是一个简单工工厂模式吗?但是仔细看看其他人的博客,发现状态模式和简单工厂模式还是有一定的区别的,最明显的是Context类持有了State,这一点和简单工厂区别很大。但是我发现状态模式又和策略模式很像,或者说是太像了。同样有抽象接口,具体实现类,Context上下文类, 以及Context里面也含有一个State抽象,简直一摸一样啊。而且网上其他很多人的博客在解释状态模式的时候,用的分明就是策略模式的例子,真的很无解!

       思量再三,对《大话设计模式》中对状态模式进行反复的研究,并且上网找了很多的微博进行阅读和理解,最终才有一点收获,并且再次进行总结。

其实看了很多的微博以后,再次来理解定义的时候,我们还是能获取一点有用的信息的:

  状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变其类【DP】

   1、"对象的内在状态":其实这和我们在策略模式中理解的具体策略,在简单工厂中的具体算法是类似的,称呼不同而已。

   2、“当一个对象的内在状态改变时允许改变其行为”: 举例说A对象内部持有的B改变了,此时允许B改变A的具体行为操作,即状态控制行为

   

适用性 

在下面的两种情况下均可使用State模式:

1) • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
2) • 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

 

下面分享我在网上找到的一个关于投票的优秀案例,我对其进行改造,看起来更加的符合状态模式定义:

 考虑一个在线投票系统的应用,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票次数超过5次,则判定为恶意刷票,要取消该用户投票的资格,当然同时也要取消他所投的票;如果一个用户的投票次数超过8次,将进入黑名单,禁止再登录和使用系统。

  要使用状态模式实现,首先需要把投票过程的各种状态定义出来,根据以上描述大致分为四种状态:正常投票、反复投票、恶意刷票、进入黑名单。然后创建一个投票管理对象(相当于Context)。

投票的抽象接口:

package com.sjmx.state;

public interface VoteState {

    /**
     * 处理状态对应的行为
     * 
     * @param user
     *            投票人
     * @param voteItem
     *            投票项
     * @param voteManager
     *            投票上下文,用来在实现状态对应的功能处理的时候, 可以回调上下文的数据
     */
    public void vote(String user, String voteItem, VoteManager voteManager,int count);
}

具体状态类——正常投票:

package com.sjmx.state;

/*
 * 
 * 具体状态类——正常投票
 */

public class NormalVoteState implements VoteState {

    @Override
    public void vote(String user, String voteItem, VoteManager voteManager,int count) {
        // 正常投票,记录到投票记录中
        if(1 == count){
            voteManager.getMapVote().put(user, voteItem);
            System.out.println("恭喜投票成功,投票内容为:" + voteItem);
            
            voteManager.state = new RepeatVoteState();
        } 
    }
}

具体状态类——重复投票:

package com.sjmx.state;

/*
 * 
 *  具体状态类——重复投票
 */

public class RepeatVoteState implements VoteState {

    @Override
    public void vote(String user, String voteItem, VoteManager voteManager,int count) {
        
        // 重复投票,暂时不做处理
        if(count > 1 && count < 5){
            System.out.println("请不要重复投票,投票内容为:" + voteItem);
        }else{
            voteManager.state = new SpiteVoteState();
            voteManager.voteByChange(user, voteItem, count);
        }
    }
}

恶意投票:

package com.sjmx.state;

public class SpiteVoteState implements VoteState {
    
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager,int count) {
        
        // 恶意投票,取消用户的投票资格,并取消投票记录
        if(count >= 5 && count < 8)
        {
            System.out.println("count:" + count);
            String str = voteManager.getMapVote().get(user);
            if (str != null) {
                voteManager.getMapVote().remove(user);
            }
            System.out.println("你有恶意刷屏行为,取消投票资格!-----投票内容为:" + voteItem);
        }else{
            voteManager.state = new BlackVoteState();
            voteManager.voteByChange(user, voteItem, count);
        }
    }
}

记录黑名单中,禁止登录系统:

package com.sjmx.state;

public class BlackVoteState implements VoteState {

    @Override
    public void vote(String user, String voteItem, VoteManager voteManager,int count) {
        
        // 记录黑名单中,禁止登录系统
        System.out.println("进入黑名单,将禁止登录和使用本系统,投票内容为:" + voteItem);
    }

}

状态管理类:

package com.sjmx.state;

import java.util.HashMap;
import java.util.Map;

public class VoteManager {
    
    // 持有状体处理对象
    public VoteState state = null;
    
    // 记录用户投票的结果,Map<String,String>对应Map<用户名称,投票的选项>
    public Map<String, String> mapVote = new HashMap<String, String>();
    
    // 记录用户投票次数,Map<String,Integer>对应Map<用户名称,投票的次数>
    public Map<String, Integer> mapVoteCount = new HashMap<String, Integer>();
    
    public VoteManager(String user) {
        state =  new NormalVoteState();
    }
    
    /**
     * 获取用户投票结果的Map
     */
    public Map<String, String> getMapVote() {
        return mapVote;
    }

    /**
     * 投票
     * @param user  投票人
     * @param voteItem投票的选项
     */
    
    public void vote(String user, String voteItem) {
        
        // 1.为该用户增加投票次数
        // 从记录中取出该用户已有的投票次数
        Integer oldVoteCount = mapVoteCount.get(user);
        if (oldVoteCount == null) {
            oldVoteCount = 0;
        }
        oldVoteCount += 1;
        mapVoteCount.put(user, oldVoteCount);
        
        this.voteByChange(user, voteItem, oldVoteCount);
    }
    
    
    public void voteByChange(String user, String voteItem, int count){
        // 然后转调状态对象来进行相应的操作
        state.vote(user, voteItem, this,count);
    }
    
    
    
    
    
}

客户端:

package com.sjmx.state;

public class Client {

    public static void main(String[] args) {
        
        VoteManager vm = new VoteManager("jack");
        for(int i=1;i<11;i++){
            vm.vote("u1","A"+i);
        }
    }

}

运行结果:

恭喜投票成功,投票内容为:A1
请不要重复投票,投票内容为:A2
请不要重复投票,投票内容为:A3
请不要重复投票,投票内容为:A4
count:5
你有恶意刷屏行为,取消投票资格!-----投票内容为:A5
count:6
你有恶意刷屏行为,取消投票资格!-----投票内容为:A6
count:7
你有恶意刷屏行为,取消投票资格!-----投票内容为:A7
进入黑名单,将禁止登录和使用本系统,投票内容为:A8
进入黑名单,将禁止登录和使用本系统,投票内容为:A9
进入黑名单,将禁止登录和使用本系统,投票内容为:A10

案例2:模拟宾馆预定

package com.net.sjms.state;

public interface Destine {
    
    public void fixHotel(Hotel hotel, Manager manager);
    
}
package com.net.sjms.state;

public class Free implements Destine{

    @Override
    public void fixHotel(Hotel hotel, Manager manager)
    {
         if(State.free.toString().equals(hotel.getState()))
         {
             System.out.println("房间状态为:" + hotel.getState() + ", 预定成功");
             hotel.setState(State.full.toString());
         }else
         {
             manager.dest = new Full();
             manager.desHotel();
         }
    }
    
    
    
}
package com.net.sjms.state;

public class Full implements Destine {

    @Override
    public void fixHotel(Hotel hotel, Manager manager)
    {
        if(State.full.toString().equals(hotel.getState()))
        {
            System.out.println("对不起,房间已满,无法完成预定!");
        }else{
             manager.dest = new Free();
             manager.desHotel();
        }
    }

}

房间状态:

package com.net.sjms.state;

public enum State
{
     free,full
}
package com.net.sjms.state;

public class Hotel {
    
    private String name;
    
    public String state;
    
    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
    
    public String getState()
    {
        return state;
    }

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

}

上下文类:

package com.net.sjms.state;
 
public class Manager {
    
    public Destine dest;
    
    public Hotel hotel;
    
    public Manager()
    {
        this.dest = new Free();
        this.hotel = new Hotel();
        this.hotel.setState(State.free.toString());
    }
    
    public void desHotel()
    {
        dest.fixHotel(hotel,this);
    }
}

客户端:

package com.net.sjms.state;

public class Client {
    
    public static void main(String[] args)
    {
        final Manager manager = new Manager();
        
        //启动另一个线程,模拟一段时间以后,房客会退房
        new Thread(new Runnable() {
            @Override
            public void run()
            {
                while(true)
                {
                    try {
                        Thread.sleep(500);
                        manager.hotel.setState(State.free.toString());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        
        for(int i=0; i <30; i++)
        {
            try {
                manager.desHotel();
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
}

运行结果:

房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
房间状态为:free, 预定成功
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
对不起,房间已满,无法完成预定!
原文地址:https://www.cnblogs.com/chen1-kerr/p/7093976.html