• 策略模式(Strategy) 对象的行为型模式


    述:

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

    意图:

    • 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。
    • 策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
    • 运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。

     

    参与者:

    • 环境(Context)角色:需要使用ConcreteStrategy提供的算法;内部维护一个Strategy的实例;负责动态设置运行时Strategy具体的实现算法;负责跟Strategy之间的交互和数据传递。
    • 抽象策略(Strategy)角色:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
    • 具体策略(ConcreteStrategy)角色:实现了Strategy定义的接口,提供具体的算法实现。

    UML图:

    实质:

      策略模式中的Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类,而是将生成类所实现的功能接口包装一次,提供给客户。这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。

     

        public interface IStrategyA
        {
            //该方法对于Context来说,该行为不稳定,是动态的,如果在运行时才确定具体行为,所以用has-a 的关系会比is-a的关系好
            void AlgorithmA();
        }
    
        public interface IStrategyB
        {
            //该方法对于Context来说,该行为不稳定,是动态的,如果在运行时才确定具体行为,所以用has-a 的关系会比is-a的关系好
            void AlgorithmB();
        }
    
        public class StrategyA:IStrategyA{
    
            public void AlgorithmA()
            {
                //具体实现策略A
            }
        }
    
        public class StrategyAA : IStrategyA
        {
            public void AlgorithmA()
            {
                //具体实现策略AA
            }
        }
    
        public class StrategyB : IStrategyB
        {
            public void AlgorithmA()
            {
                //具体实现策略B
            }
        }
    
        public class StrategyBB : IStrategyB
        {
            public void AlgorithmB()
            {
                //具体实现策略BB
            }
        }
        public interface IAction
        {
            //该方法对于Context来说很稳定,该行为确定了,客户端不需要动态改变其行为,所以用is-a的关系会比has-a的关系好
            void Action();
        }
    //AContext具备IAction、IStrategyA、IStrategyB接口行为
        public abstract class AContext:IAction
        {
            //如果IStrategy行为是不固定的。继承IStrategyA、IStrategyB方式也可以实现该功能,但是需要为每一个特殊的子类提供具体行为的实现。
            IStrategyA strategyA;
            //而且如果象这种IStrategy有多种的话,继承代码重用功力很差了
            IStrategyB strategyB;
            public AContext(IStrategyA strategyA,IStrategyB strategyB)
            {
                this.strategyA = strategyA;
                this.strategyB = strategyB;
            }
    
            public void ContextInterfaceA()
            {
                strategyA.AlgorithmA();
            }
    
            public void ContextInterfaceB()
            {
                strategyB.AlgorithmB();
            }
    
            public void Action()
            {
               //具体Action行为
            }
        }

    优点:

    •  提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。

             我的理解:结合上序代码,如果多项行为在子类里持续不断地改变在运行时才确定,所以让所有的子类都拥有基类的行为是不适当的,而使用实现接口的方式,又破坏了代码重用。找到系统中变化的部分,将变化的部分同其它稳定的部分隔开。变化的部分就是子类里的行为,所以我们要把这部分行为封装起来,去动态地改变一个子类的行为,运行时确定具体子类实例的行为。通过继承的话我们则需要为每一个特殊的子类提供具体行为的实现。

    • 简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
    • 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
    • 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

     

    缺点:

    • 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
    • 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。(这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就最大化的减轻了客户端的压力。)

    使用场景:

    • 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
    • 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(具体实现可任意变化或扩充)
    • 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。

    简单工厂改进策略模式(也可以通过反射改进,这里不再介绍)

    View Code
     //简单工厂改进策略模式(也可以通过反射改进,这里不再介绍)
        public abstract class AContextFactory : IAction
        {
            IStrategyA strategyA;
            IStrategyB strategyB;
            public AContextFactory(string typeA, string typeB)
            {
                switch (typeA)
                {
                    case "StrategyA":
                        this.strategyA = new StrategyA();
                        break;
                    case "StrategyAA":
                        this.strategyA = new StrategyAA();
                        break;
                }
                switch (typeB)
                {
                    case "StrategyB":
                        this.strategyB = new StrategyB();
                        break;
                    case "StrategyBB":
                        this.strategyB = new StrategyBB();
                        break;
                }
            }
    
            public void ContextInterfaceA()
            {
                strategyA.AlgorithmA();
            }
    
            public void ContextInterfaceB()
            {
                strategyB.AlgorithmB();
            }
    
            public void Action()
            {
                //具体Action行为
            }
        }

    总结:以上纯属个人的理解,对于有些地方觉得还是理解不是很深,有不足之处和错误的地方希望大家帮我指出。谢谢

  • 相关阅读:
    小程序 生成二维码
    uni-app调用wifi接口
    微信小程序代码上传,审核发布小程序
    uni-app开发经验分享十五: uni-app 蓝牙打印功能
    面试题 16.11. 跳水板
    LeetCode 63. 不同路径 II
    LeetCode 44. 通配符匹配
    LeetCode 108. 将有序数组转换为二叉搜索树
    LeetCode 718. 最长重复子数组
    LeetCode 814. 二叉树剪枝
  • 原文地址:https://www.cnblogs.com/gyb333/p/StrategyPattern.html
Copyright © 2020-2023  润新知