• 设计模式


    前言:

      先不管模式, 把他和他的名字都忘了, 来看看问题 和 设计思路. 为啥要这么做. 

    场景:

      有一家店铺, 里面有一个售货员, 售货员当然是要卖东西的啦, 客户进来买完东西, 找售货员结账, 那售货员得知道一共多少钱吧?

    一. 初步设计

      商品类:

    package org.elvin.strategy;
    
    /***/
    public class Goods {
        /**
         * 商品名
         */
        private String name;
    
        /**
         * 商品价格
         */
        private Long Price;
    
        public Goods() { }
    
        public Goods(String name, Long price) {
            this.name = name;
            Price = price;
        }
    
        //region getter / setter
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Long getPrice() {
            return Price;
        }
    
        public void setPrice(Long price) {
            Price = price;
        }
        //endregion
    }

    由于价格我使用的是 Long 类型, 所以, 要有一个转换输出的方法.

    package org.elvin.strategy;
    
    import java.text.MessageFormat;
    
    public class MoneyUtils {
        public static String getYuan(Long money){
            Long yuan = money / 100;
            Long jiao = money % 100 /  10;
            Long fen = money % 10;
    
            return MessageFormat.format("{0}.{1}{2}", yuan, jiao , fen );
        }
    }

    售货员:

    package org.elvin.strategy;
    
    import org.elvin.strategy.calculator.*;
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 环境角色(Context)*/
    public class Seller {
    
        /**
         * 姓名
         */
        private String name;
    
        /**
         * 编号
         */
        private String code;

    //region getter / setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; }
    //endregion /** * 售卖商品 */ public void sellGoods(List<Goods> goods){ Long sum = 0L; for (Goods good : goods) { sum += good.getPrice(); } System.out.println("应付款: " + MoneyUtils.getYuan(sum) + " 元"); } @Test public void func1(){ List<Goods> goods = new ArrayList<>(); goods.add(new Goods("泡面", 550L)); goods.add(new Goods("泡面", 550L)); goods.add(new Goods("泡面", 550L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("火腿", 150L)); goods.add(new Goods("鸡蛋", 70L)); goods.add(new Goods("鸡蛋", 70L)); goods.add(new Goods("鸡蛋", 70L)); goods.add(new Goods("饼干", 250L)); goods.add(new Goods("辣条", 300L)); sellGoods(goods); } }

    来看一下计算结果:

    得到结果了, 没啥毛病, 挺好. 

    今天老板过生日, 突然想到, 要不要再店里搞个活动, 来个优惠活动, 有些商品减价销售. 

    明天老板娘过生日, 老板娘说, 老娘高兴, 你减价销售, 明天我来个免单销售.

    那现在咋办呢? 明天过后, 价格肯定又要恢复到正常价格.  what the fuck! 

    Seller类写死了, 难道我在里面加几个计算方法? 

    加进去貌似可以解决当下问题, 但是, 后天如果老板的老丈人过生日呢? 咋搞?

    Ok, 到这里, 差不多, 需要请出今天的大神 : 策略模式. 让他来帮我们解决这个问题吧.

    二. 设计改造

    对于售货员来说, 她必须知道, 今天该怎么计算价格, 是优惠还是不优惠, 或者是满多少钱, 送东西什么的. 

    那么, 将这些优惠或者说价格的计算方法抽象出来, 成为一个接口或者抽象类. 

    让优惠或者不优惠实现或者继承他.

    实现方式:

    这里, 我将他抽象为一个接口

    package org.elvin.strategy.calculator;
    
    import org.elvin.strategy.Goods;
    
    import java.util.List;
    
    /**
     * 抽象策略角色(Strategy)
     * 优惠接口
    */ public interface PreferentialPrice { public void getPrice(List<Goods> goods); }
    PreferentialPrice 需要作为一个属性,出现在 Seller 类中.

    在Seller中加入

        /**
         * 计算优惠后的价格
         * 抽象角色, 次角色给出所有具体策略类所需的接口
         */
        private PreferentialPrice preferentialPrice;
    
        public PreferentialPrice getPreferentialPrice() {
            return preferentialPrice;
        }
    
        public void setPreferentialPrice(PreferentialPrice preferentialPrice) {
            this.preferentialPrice = preferentialPrice;
        }

    这里提供三种计算方式:

    1. 正常方式

    /**
     * 具体策略角色(ConcreteStrategy)
    */ public class NoPreferential implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { Long sum = 0L; for (Goods good : goods) { sum += good.getPrice(); } System.out.println("应付款: " + MoneyUtils.getYuan(sum) + " 元"); } }

    2. 免单方式

    /**
     * 具体策略角色(ConcreteStrategy)
    */ public class Free implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { System.out.println("免单, 不要钱 !"); } }

    3. 部分优惠方式

    /**
     * 具体策略角色(ConcreteStrategy)
    */ public class ReduceSomeGoods implements PreferentialPrice { @Override public void getPrice(List<Goods> goods) { Long sum = 0L; for (Goods good : goods) { switch (good.getName()) { case "泡面": sum += good.getPrice() - 50L; break; case "火腿": sum += good.getPrice() - 20L; break; case "鸡蛋": sum += good.getPrice() - 10L; break; default: sum += good.getPrice(); break; } } System.out.println("应付款: " + MoneyUtils.getYuan(sum) + " 元"); } }

      

     将Seller类中, 计算的方法修改一下:

        public void sellGoods(List<Goods> goods){
            if(preferentialPrice == null){
                setPreferentialPrice(new NoPreferential());
            }
            preferentialPrice.getPrice(goods);
        }

    在计算的时候, 如果没有传入优惠, 则默认使用无优惠方式

    再看测试方法:

        @Test
        public void func1(){
            List<Goods> goods = new ArrayList<>();
            goods.add(new Goods("泡面", 550L));
            goods.add(new Goods("泡面", 550L));
            goods.add(new Goods("泡面", 550L));
            goods.add(new Goods("火腿", 150L));
            goods.add(new Goods("火腿", 150L));
            goods.add(new Goods("火腿", 150L));
            goods.add(new Goods("鸡蛋", 70L));
            goods.add(new Goods("鸡蛋", 70L));
            goods.add(new Goods("鸡蛋", 70L));
            goods.add(new Goods("饼干", 250L));
            goods.add(new Goods("辣条", 300L));
    
            setPreferentialPrice(new Free());
            sellGoods(goods);
            System.out.println("-----------------------");
            setPreferentialPrice(new ReduceSomeGoods());
            sellGoods(goods);
            System.out.println("-----------------------");
            setPreferentialPrice(new NoPreferential());
            sellGoods(goods);
        }

    结果:

      

    策略模式作为一种对象行为模式, 在这里应该还是体现到了吧.

    那总结一下?给个不容易懂得(网上抄的):

      策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

    说的比较抽象, 来个具体的吧:

      一帅哥喜欢约妹子, 那咋约出来呢? 不是所有的妹子都喜欢吃饭看电影吧. 那针对不同的妹子, 使用不同的方法来约. 约喜欢看电影的妹子看电影, 约喜欢吃小吃的妹子吃小吃.

    那吃饭, 看电影, 吹海风...... 等等, 这些手段, 目的都是为了让妹子做他女朋友(这里不讨论时间). 目的不变, 手段层出不穷. 这些方法, 就可以理解为不同的 strategy. 

     

    通过上面的例子, 可以看出, 具体的算法与算法之间没有依赖关系, 都是平等的(平等性), 可以相互替换的. 那在运行的时候, 每次都只能使用一种(唯一性). 

  • 相关阅读:
    URAL1996 Cipher Message 3(KMP + FFT)
    UVa12633 Super Rooks on Chessboard(容斥 + FFT)
    SPOJ TSUM Triple Sums(FFT + 容斥)
    UVa12298 Super Poker II(母函数 + FFT)
    LA4671 K-neighbor substrings(FFT + 字符串Hash)
    HDU4080 Stammering Aliens(二分 + 后缀数组)
    HDU4609 3-idiots(母函数 + FFT)
    HDU1402 A * B Problem Plus(FFT)
    快速傅里叶变换FFT学习小记
    HDU4971 A simple brute force problem.(强连通分量缩点 + 最大权闭合子图)
  • 原文地址:https://www.cnblogs.com/elvinle/p/8176225.html
Copyright © 2020-2023  润新知