• 【行为型模式】《大话设计模式》——读后感 (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, 预定成功
    对不起,房间已满,无法完成预定!
    对不起,房间已满,无法完成预定!
    对不起,房间已满,无法完成预定!
    对不起,房间已满,无法完成预定!
  • 相关阅读:
    Xilium.CefGlue怎么使用Js调用C#方法
    【转】.NET多种WebKit内核/Blink内核浏览器初步测评报告
    【转】c# winform 打包部署 自定义界面 或设置开机启动
    【转】C#程序打包安装部署之添加注册表项
    【转】C# Winform打包部署时添加注册表信息实现开机启动
    输出数组全排列
    卡特兰数相关问题
    Keras 最新《面向小数据集构建图像分类模型》
    Keras使用的一些细节
    转置卷积&&膨胀卷积
  • 原文地址:https://www.cnblogs.com/chen1-kerr/p/7093976.html
Copyright © 2020-2023  润新知