• 第二节:策略模式详解


    1. 背景

      在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

      在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。

    2. 定义和特点

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

    (2) 优点:

     A. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。

     B. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。

     C. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。

     D. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。

     E. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

    (3) 缺点

     A. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。

     B. 策略模式造成很多的策略类。

    3.具体实现

    (1) 模式的结构

     策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性。

     A. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。

     B. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。

     C. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

    结构图:

    (2) 应用场景

      不同场景下需要对两个数进行运算(加减乘除),这个时候就可以使用策略模式。首先声明一个策略接口IStrategy,用于定义策略的计算方法; 然后声明具体的策略类(加减乘除),均实现这个IStrategy策略接口;最后声明一个Context策略选择类,用户客户端调用执行。

    (3) 代码实操

    策略接口和具体的策略类

        /// <summary>
        /// 抽象策略结构
        /// </summary>
        public interface IStrategy
        {
            /// <summary>
            /// 策略计算方法
            /// </summary>
            /// <param name="num1"></param>
            /// <param name="num2"></param>
            /// <returns></returns>
            public int DoOperation(int num1, int num2);
        }
        /// <summary>
        /// 1、加法策略
        /// </summary>
        public class OperationAdd : IStrategy
        {
            public int DoOperation(int num1, int num2)
            {
                return num1 + num2;
            }
        }
        /// <summary>
        /// 2、减法策略
        /// </summary>
        public class OperationSubtract : IStrategy
        {
            public int DoOperation(int num1, int num2)
            {
                return num1 - num2;
            }
        }
        /// <summary>
        /// 3、乘法策略
        /// </summary>
        public class OperationMultiply : IStrategy
        {
            public int DoOperation(int num1, int num2)
            {
                return num1 * num2;
            }
        }
        /// <summary>
        /// 4、除法策略
        /// </summary>
        public class OperationDivision : IStrategy
        {
            public int DoOperation(int num1, int num2)
            {
                return num1 / num2;
            }
        }

    策略选择类

        /// <summary>
        /// 策略选择类
        /// </summary>
        public class Context
        {
            private IStrategy strategy;
    
            public Context(IStrategy strategy)
            {
                this.strategy = strategy;
            }
    
            public int executeStrategy(int num1, int num2)
            {
                return strategy.DoOperation(num1, num2);
            }
        }

    测试

    {
                    Context context = new Context(new OperationAdd());
                    Console.WriteLine("10 + 5 = " + context.executeStrategy(10, 5));
    
                    context = new Context(new OperationSubtract());
                    Console.WriteLine("10 - 5 = " + context.executeStrategy(10, 5));
    
                    context = new Context(new OperationMultiply());
                    Console.WriteLine("10 * 5 = " + context.executeStrategy(10, 5));
    
                    context = new Context(new OperationDivision());
                    Console.WriteLine("10 / 5 = " + context.executeStrategy(10, 5));
    
                }

    运行结果

    4. 适用场景分析

     A. 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。

     B. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。

     C. 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。

     D. 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。

     E. 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    UART中的硬件流控RTS与CTS
    Yii smsto短信接口的函数
    分布式选主 利用Mysql ACID和Lease协议实现选主和高可用
    Extjs GridPanel 合计功能 解决滚动条滚动问题和页面刷新滚动条回到初始位置问题。
    基于ARM+LINUX的无线视频采集系统设计搭建嵌入式web服务器
    C++里父类的析构函数为什么声明为virtual
    重量过载 = 重载
    Spread Studio中文支持图解
    Android用户如何FQ访问被封锁的媒体资源
    将myeclipse 10.x以下版本web project的导入到myeclipse blue 2013 部署没有项目名
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/13444014.html
Copyright © 2020-2023  润新知