策略模式

策略模式

一、策略模式简介

定义:策略模式定义了一系列的算法,并将所有算法封装起来,而且它们之间可以相互替换,让算法独立于使用它的客户而独立变化。

分析:算法之间可以相互替换,也就是说它们之间有共性,它们共性体现在策略接口的行为上,为了 让算法独立于使用它的客户而独立变化 这个句话,那么我们要让客户端依赖于策略接口。

直白点说:就是客户只管他需要什么算法,得到对应的正确的计算结果就行。不用管内部是怎么实现或做到的。

使用场景:

1.同类型的问题,但不同的处理方式,仅仅是具体逻辑或行为上的差别。

2.需要安全的封装多种同类型的操作时。

3.出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。

废话不多说,我们看下实际例子:

二、实际 Demo

原需求:以面向对象的思想设计一个价格计算器。

考虑过程:

既然是价格计算器,那么我们界面上只需要让物品数量、价格。就可以直接计算出结果。代码略...

需求变更1.0:计算器可以带上打折功能。

既然是带打折功能,那么我们直接在上一个需求的基础上,添加多一个打折选项,计算过程再修改下,就能正常跑起来了。(注意,此时需求已经发生变化了,但我们还在修改原来的类,违反了单一职责、开闭原则。坏代码的味道出现了!!!)

代码略....

需求变更2.0:计算器还可以做满减活动,如满 200-20 的优惠。

!!!

问题出现了,如果我们还继续在上一个代码的基础上改,那么下次如果再一次出现新的需求,比如:又要可以满减,又可以打折,还有积分功能。请问,你怎么办?还在原代码改?继续改下去?这样难免会出现遗漏、逻辑出现错误,而且可能会出现,修复了一个 BUG 之后,另一个地方不小心被我们影响到了,导致出现一个新的 BUG 。

那么,这时候,我们的 策略模式 就应该出场了。

我们先进行一波分析:

1.计算器的计算方法是变化的。

2.计算方法有多种,也就是说策略是有多种。

我们来实际写一个代码吧:

首先,我们定义一个抽象的策略类:

        /// <summary>
        /// 抽象策略类
        /// </summary>
        public abstract class cacleStrategy
        {
            /// <summary>
            /// 取得最终的金额 的 抽象方法
            /// </summary>
            /// <param name="money">实际要计算的金额</param>
            /// <returns>计算结果金额</returns>
            public abstract double getResultMoney(double money);
        }

接下来,我们来实现打折的算法:

        /// <summary>
        /// 打折计算 算法类
        /// </summary>
        public class discountedCacle : cacleStrategy
        {
            /// <summary>
            /// 折扣
            /// </summary>
            private double _discountedPercent { get; set; }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="discountedPercent">折扣</param>
            public discountedCacle(double discountedPercent)
            {
                _discountedPercent = discountedPercent;
            }


            /// <summary>
            /// 计算打折的结果
            /// </summary>
            /// <param name="money"></param>
            /// <returns></returns>
            public override double getResultMoney(double money)
            {
                return money * _discountedPercent;
            }
        }

接下来,我们实现满减的算法:

        /// <summary>
        /// 满减算法 类
        /// </summary>
        public class fullMinusCacle : cacleStrategy
        {
            /// <summary>
            /// 最少开始满减的金额
            /// </summary>
            private double _fullMinPrice { get; set; }

            /// <summary>
            /// 减多少钱
            /// </summary>
            private double _fullMinusPrice { get; set; }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="fullMinPrice">最少开始满减的金额</param>
            /// <param name="fullMinusPrice">减多少钱</param>
            public fullMinusCacle(double fullMinPrice, double fullMinusPrice)
            {
                _fullMinPrice = fullMinPrice;
                _fullMinusPrice = fullMinusPrice;
            }

            /// <summary>
            /// 计算结果
            /// </summary>
            /// <param name="money"></param>
            /// <returns></returns>
            public override double getResultMoney(double money)
            {
                if (money >= _fullMinPrice)
                {
                    return money - _fullMinusPrice;
                }
                else
                {
                    return money;
                }
            }

        }

现在我们有了抽象策略类,实际实现的策略类,那么,我们需要一个上下文对象来实例化这些东西:

        /// <summary>
        /// 策略上下文类
        /// </summary>
        public class CacleContext
        {

            private cacleStrategy _strategy;


            public CacleContext(cacleStrategy cacleStrategy)
            {
                _strategy = cacleStrategy;
            }

            public double getResultMoney(double money) 
            {
                return _strategy.getResultMoney(money);
            }
        }

我们来测试下:

        static void CacleContextMain(string[] args)
        {
            double price=510;
            Console.WriteLine("1.满减功能:");
            cacleStrategy fullMoney = new fullMinusCacle(100, 20);
            double resultMoney= fullMoney.getResultMoney(price);
            Console.WriteLine("最终结果:"+resultMoney);


            Console.WriteLine("2.打折功能:");
            cacleStrategy discountMoney = new discountedCacle(0.9);

            resultMoney= discountMoney.getResultMoney(price);
            Console.WriteLine("最终结果:" + resultMoney);


            Console.Read();
        } 

结果我就不写出来了,代码简单,跑一下就知道了。

好了,这样,我们就实现了 策略模式,但!还记得我们上一次写的简单工厂么?不觉得这种写法很熟悉么?可以把我们的简单工厂和策略模式整合在一起。

下面是 Demo :

        /// <summary>
        /// 策略工厂
        /// </summary>
        public class cacleFactory
        {
            private cacleStrategy _cacle;
            public cacleFactory(string type)
            {
                switch (type)
                {
                    case "full":
                        _cacle = new fullMinusCacle(100, 10);
                        break;
                    case "discounted":
                        _cacle = new discountedCacle(0.9);
                        break;
                    default:
                        break;
                }
            }

            public double getResult(double money)
            {
                return _cacle.getResultMoney(money);
            }
        }

调用一下:

        static void StrategyMain(string[] args)
        {
            var cacleItem = new cacleFactory("full");
            Console.WriteLine(cacleItem.getResult(510));

            cacleItem = new cacleFactory("discounted");
            Console.WriteLine(cacleItem.getResult(510));

            Console.Read();
        } 

这样,我们的 策略模式 就和 工厂模式 整合在一起了,实例化的过程都在工厂里面完成了,我们可以从配置文件中读取那些具体的折扣要求、满减数之类的。以后,如果还需要增加一个会员积分功能,那么我们可以写多一个策略类去实现具体的逻辑,再改写一下工厂类,原来的代码基本不会动到,出错的几率大大降低了。

本人实际用例:

极光推送(如果不了解推送的可以先去百度了解一下)

我是做智能家居的,里面有个模块用到了推送功能,像开关门报警,燃气报警,SOS 紧急报警 等等。都要用到极光推送,但它们具体的数据逻辑又不一样,我们需要一个个去判断,去处理。原先没学习策略模式时,一堆 if else if else ...代码不好维护,并且之前有试过增加了一个新的推送信息,导致原先的部分推送失效,查 BUG 浪费了点时间。改进后,如有新的设备或推送要求,原先已经写好的策略类不变,只增加新的策略类,修改工厂类,我们就完成需求,工作量小了不少,而且不容易出错,以下为代码的部分截图。不用吐槽代码命名规则之类的,写得不好。

 原先写法:

 修改后:

 感谢大家的观看。

原文地址:https://www.cnblogs.com/Frank-Jan/p/8872384.html