zoukankan      html  css  js  c++  java
  • 设计模式策略模式

    策略模式:

    策略模式定义了算法家族,分别封装起来,让他们可以相互替换,此模式让算法变化,但不会影响到使用该算法的用户.

    结构图:

    image 

    strategy就是个策略类,里面记录了客户需要的方法,此时,客户不一定指客户端,而是指调用这个类的类,我们也可以称之为客户.策略类是个抽象类(抽象策略类),并不实现任何方法,由其具体子类(具体策略类)来实现.

    Context只需要保持一个对策略类的引用,然后在context类中调用即可,返回抽象策略类中定义的方法.

    适用场景:

    1.在运行时需要动态的设置行为时

    2.对客户隐藏类的详细实现

    优点:

    1.行为的实现是通过组合,而不是继承,更有利于代码的复用

    2.策略测试更加容易,只需单独的测试每个策略即可.在增加新的策略的情况下,也只需要对新增的策略进行测试,而不用对所有的策略进行测试

    3.有效的提供了代码的封装,同时,当策略增加或者减少时,都不会影响到Context类的变化避免了对if else或者switch语句的判断操作.

    缺点:

    1.当增加策略时,会增加相应的类,维护系统的任务要加重

    2.策略模式需要客户端知道所有的策略(行为)

    例子:

    人下班总是要回家的,有人走路回家,有人骑自行车回家,有人开车回家,该如何写呢?

    public class Person { public void GoHomeOnFoot() { //TODO } public void GoHomeByBike() { //TODO } public void GoHomeWithCar() { //TODO } }

    也可以这么定义这个类:

    public class Person
    {
        public string GoHome(string type)
        {
            switch (type)
            {
                case "foot": return "onfoot";
                case "car": return "withcar";
                case "bike": return "bybike";
                default: return string.Empty;
            }
        }
    }

    那么页面调用时,只需要写:

    Person p = new Person(); p.GoHomeWithCar(); or Person p = new Person(); p.GoHome("car");

    即可.但是,当增加新的回家方式(GoHomeByBus)时,该怎么办呢?修改Person类,同时修改客户端.

    这就违背了 开放封闭原则.

    采用策略模式,该如何处理呢?

    不论是GoHomeOnFoot,还是GoHomeByBike,还是GoHomeWithCar,其实都是回家,只不过是回家的方式不一样.而且,这三种方式回家有什么关系呢?没有关系,没有关系的话,就可以将他们分开至每一个类.

    image

    代码:

    public abstract class GoHome
    {
        public abstract string Go();
    }
    
    public class GoHomeWithCar : GoHome
    {
        public override string Go()
        {
            return "GoHomeWithCar";
        }
    }
    
    public class GoHomeByBike : GoHome
    {
        public override string Go()
        {
            return "GoHomeByBike";
        }
    }
    
    public class GoHomeOnFoot : GoHome
    {
        public override string Go()
        {
            return "GoHomeOnFoot";
        }
    }
    
    
    public class Person
    {
        GoHome context;
    
        public Person()
        {
            
        }
    
        public Person(GoHome context)
        {
            this.context = context;
        }
    
        public void SetGoHomeBehavior(GoHome context)
        {
            this.context = context;
        }
    
        public string GoHome()
        {
            return context.Go();
        }
    }

    页面调用:

    protected void Page_Load(object sender, EventArgs e) { GoHome g = new GoHomeByBike(); Person p = new Person(g); Display(p); p.SetGoHomeBehavior(new GoHomeWithCar()); Display(p); } private void Display(Person p) { Response.Write(p.GoHome()+"<br/>"); }

    结果:

    GoHomeByBike GoHomeWithCar

    这样设计的话,当新增一个GoHomeByBus方法时,只需要建立一个类,继承GoHome类即可.实现Go方法.

    页面调用处,在调用到该方法时,进行修改即可.此时避免了修改原文中的Person类,保证了其他几种回家方式的正确性.确保回家方式被扩展时,它们没有受到影响.

    对于回家方式.可以再页面设置一个DropDownList,对回家方式进行绑定.同时,采用反射机制,基本可以保证客户端也不需要什么改变,只需要增加回家方式即可.

    涉及到的设计原则

    1.多用组合,少用继承

    组合是一种“HAS-A”关系,而继承是一种“IS-A”关系。很明显“HAS-A”要比“IS-A”更灵活一些。

    也就是说在创建系统的时候,我们应该优先使用对象组合,因为它不仅可以给你提供更多灵活性和扩展性,而且还使你可以在运行时改变行为(组合不同的对象)!但是也不是说继承就是不能用,只是说应该把继承应用在相对更稳定,几乎没有变化的地方.

    2.找到系统中变化的部分,将变化的部分同稳定的部分分开.

    稳定的部分可以通过继承的方式来实现.而变化的部分,则使用策略模式.

    3.面向接口编程,而不是面向实际编程

    此处的面向接口编程,并不单单指接口,而是指超类,接口.

    面向接口编程时,我们可以使用多态,通过一些其他的简单模式,可以再客户端不需要改变的时候,对系统进行扩展.

    面向实际编程,就想   实际 连接  实际

    面向接口编程,就像  实际  连接  接口(超类)  连接 实际

    接口在其中搭起了一座桥梁.在其中,可以根据客户的需要,具体的调用哪个实际类

    面相接口编程中,并不代表不会使用new,new是不可避免要使用的.除非总是使用静态方法.面向接口编程时,是指实际客户不用new一个实际,而会通过其他方式获得该实际的引用,或方法的引用.从而实现解耦合.

    下面一段话摘自<<大话设计模式>>一书,做了部分修改:

    在软件设计时,有时不得不在客户端的代码中为了判断用哪个算法而使用了switch,或者if..else..,这是正常的,因为当不同的行为堆砌在一个类中的时候,就很难避免使用条件语句来选择合适的行为.将这些行为封装在一个个独立的strategy类中,可以在使用这些行为的类中消除条件语句.

    策略模式就是用来封装算法,我们可以用它来封装任何类型的规则,只要在分析过程中听到需要在不同时间应用不用的业务规则,就可以考虑使用策略模式来 处理

    任何修改都是需要成本的.

    高手和菜鸟的区别就在于高手可以花同样的代价获得最大的利益.或者说,对于同样的需求改变,改动越少越好.

  • 相关阅读:
    Filebeat Processors对日志数据的处理
    beats直接给es传输日志,自定义索引名
    Elasticsearch:修改fielddata
    Elasticsearch:Elasticsearch中的refresh和flush操作指南
    Elasticsearch创建索引(index)及一个文档(document)
    Elasticsearch:如何对PDF文件进行搜索
    C++ 类构造函数 & 析构函数~
    学习CSS的好地方:CSS Inspiration -- CSS灵感
    css式样里的content
    寄存器与cmp,mov,add,sub,IMUL指令
  • 原文地址:https://www.cnblogs.com/oneword/p/1446792.html
Copyright © 2011-2022 走看看