[30 days to get started quickly TDD] [Day 17] Refactoring - Strategy Pattern

[30 days to get started quickly TDD] [Day 17] Refactoring - Strategy Pattern


Foreword

In the last article, we will target the various logistics providers, abstracted out a logistics provider interface, this interface provides the functionality required for the current page object:

  1. Calculate shipping
  2. Freight get results
  3. Made logistics business name

Although the page object still directly dependent and logistics business objects, but in the context end already "user interface", and the logistics business practice regardless of the objects behind.

This article, though with the title "Strategy Pattern", that is, the policy mode, but readers not familiar with Design Patterns friends do not worry, maintained a mind free from strokes can be. We just need to put the bad taste of a program of reconstruction in the most natural way, you will appreciate the Strategy Pattern look, purpose, usage, Strategy Pattern will automatically emerge.

Remember, although no mind trick, but there is still the Heart, which is the SOLID principles OO is our reconstruction of the floor.

Just a predicate reconstruction, keep to the same thing, the same thing is not drawn into a function, I think ... three minutes should still be very good enough

Current procedures

To facilitate comparison of the reconstruction program before and after the reading, here are listed first date, our page program are as follows:


protected void btnCalculate_Click(object sender, EventArgs e)
{
    //若页面通过验证
    if (this.IsValid)
    {
        //取得画面数据
        var product = this.GetProduct();

        var companyName = "";
        double fee = 0;

        //选黑猫,计算出运费
        if (this.drpCompany.SelectedValue == "1")
        {
            //计算
            //BlackCat blackCat = new BlackCat() { ShipProduct = product };
            //blackCat.Calculate();
            //companyName = blackCat.GetsComapanyName();
            //fee = blackCat.GetsFee();
            ILogistics logistics = new BlackCat() { ShipProduct = product };
            logistics.Calculate();
            companyName = logistics.GetsComapanyName();
            fee = logistics.GetsFee();


        }
        //选新竹货运,计算出运费
        else if (this.drpCompany.SelectedValue == "2")
        {
            //计算
            //Hsinchu hsinchu = new Hsinchu() { ShipProduct = product };
            //hsinchu.Calculate();
            //companyName = hsinchu.GetsComapanyName();
            //fee = hsinchu.GetsFee();

            ILogistics logistics = new Hsinchu() { ShipProduct = product };
            logistics.Calculate();
            companyName = logistics.GetsComapanyName();
            fee = logistics.GetsFee();
        }
        //选邮局,计算出运费
        else if (this.drpCompany.SelectedValue == "3")
        {
            //计算
            //PostOffice postOffice = new PostOffice() { ShipProduct = product };
            //postOffice.Calculate();
            //companyName = postOffice.GetsComapanyName();
            //fee = postOffice.GetsFee();

            ILogistics logistics = new PostOffice() { ShipProduct = product };
            logistics.Calculate();
            companyName = logistics.GetsComapanyName();
            fee = logistics.GetsFee();
        }
        //发生预期以外的状况,呈现警告消息,回首页
        else
        {
            var js = "alert('发生不预期错误,请洽系统管理者');location.href='http://tw.yahoo.com/';";
            this.ClientScript.RegisterStartupScript(this.GetType(), "back", js, true);
        }

        //呈现结果
        this.SetResult(companyName, fee);
    }
}

review

Reconstruction to this, in fact, very adequate, and reference have been isolated, but also to reduce coupling through the interface, there are corresponding integration testing unit tests.

But as a start point of said reconstructed time, when we modify the function to demand or bug, in fact, you can think again, so a similar demand will not happen again. Such a situation, there is no suitable pattern can solve our needs and problems.

Then switch back to first person mode, in front of the functional requirements, is described employing words: ' different logistics providers, using corresponding valuation method '. Design Pattern for with the words, is: ' according to the conditions, the algorithm determines the corresponding '. That is the strategy pattern (strategy pattern).

Although referred to the policy mode, but readers not familiar with Design Patterns friends do not worry, we only need to take the bad taste of a program of reconstruction in the most natural way, you will appreciate the Strategy Pattern look, purpose, usage, the Strategy Pattern It will automatically emerge.

Reconstruction of ninth-style: using Design Pattern- strategy pattern

As already mentioned, this program nutshell, is "different logistics providers, using the corresponding valuation methods," Let us now look back at the program, which is part of the same, which parts are different, As shown below:

Different portions of the same

After you can see the abstract user interface, the red box in the program, has been exactly the same as. Different sections of the program is the yellow box, which is above human speech described "the choice of different logistics providers, to use different valuation methods."

As DRY (Do not Repeat Yourself) design principles mentioned in the design of the system, should be avoided as the same thing, but with a repetitive program. A multi-copies, on behalf demand much more needs to become part, on behalf of responsibilities does not comply with the principle of a single (SRP), also represents the situation may be tainted reform.

With this example, the smart as you readers, know for sure how the different parts of the same part, be able to get a function, simply make a different argument passed to become a part of.

After reconstruction procedure is as follows:


protected void btnCalculate_Click(object sender, EventArgs e)
{
    //若页面通过验证
    if (this.IsValid)
    {
        //取得画面数据
        var product = this.GetProduct();

        var companyName = "";
        double fee = 0;

        ////选黑猫,计算出运费
        //if (this.drpCompany.SelectedValue == "1")
        //{
        //    //计算
        //    //BlackCat blackCat = new BlackCat() { ShipProduct = product };
        //    //blackCat.Calculate();
        //    //companyName = blackCat.GetsComapanyName();
        //    //fee = blackCat.GetsFee();
        //    ILogistics logistics = new BlackCat() { ShipProduct = product };
        //    logistics.Calculate();
        //    companyName = logistics.GetsComapanyName();
        //    fee = logistics.GetsFee();


        //}
        ////选新竹货运,计算出运费
        //else if (this.drpCompany.SelectedValue == "2")
        //{
        //    //计算
        //    //Hsinchu hsinchu = new Hsinchu() { ShipProduct = product };
        //    //hsinchu.Calculate();
        //    //companyName = hsinchu.GetsComapanyName();
        //    //fee = hsinchu.GetsFee();

        //    ILogistics logistics = new Hsinchu() { ShipProduct = product };
        //    logistics.Calculate();
        //    companyName = logistics.GetsComapanyName();
        //    fee = logistics.GetsFee();
        //}
        ////选邮局,计算出运费
        //else if (this.drpCompany.SelectedValue == "3")
        //{
        //    //计算
        //    //PostOffice postOffice = new PostOffice() { ShipProduct = product };
        //    //postOffice.Calculate();
        //    //companyName = postOffice.GetsComapanyName();
        //    //fee = postOffice.GetsFee();

        //    ILogistics logistics = new PostOffice() { ShipProduct = product };
        //    logistics.Calculate();
        //    companyName = logistics.GetsComapanyName();
        //    fee = logistics.GetsFee();
        //}
        ////发生预期以外的状况,呈现警告消息,回首页
        //else
        //{
        //    var js = "alert('发生不预期错误,请洽系统管理者');location.href='http://tw.yahoo.com/';";
        //    this.ClientScript.RegisterStartupScript(this.GetType(), "back", js, true);
        //}

        ILogistics logistics = this.GetILogistics(this.drpCompany.SelectedValue, product);
        if (logistics != null)
        {
            logistics.Calculate();
            companyName = logistics.GetsComapanyName();
            fee = logistics.GetsFee();

            //呈现结果
            this.SetResult(companyName, fee);
        }
        else
        {
            var js = "alert('发生不预期错误,请洽系统管理者');location.href='http://tw.yahoo.com/';";
            this.ClientScript.RegisterStartupScript(this.GetType(), "back", js, true);
        }            
    }
}

/// 
/// 将ILogistics的instance,交给工厂来决定
/// 
/// 
/// 
/// 
  
  
private ILogistics GetILogistics(string company, Product product)
{
    if (company == "1")
    {
        return new BlackCat() { ShipProduct = product };
    }
    else if (company == "2")
    {
        return new Hsinchu() { ShipProduct = product };
    }
    else if (company == "3")
    {
        return new PostOffice() { ShipProduct = product };
    }
    else
    {            
        return null;
    }
}

The same parts, i.e. the page (in this context is, usage scenarios end) functions of interest, as shown below:

context

Readers, reading from the program to calculate shipping this button logic, give it a try, feel the program will speak:

  1. If the page is verified Validation
  2. Obtain information on the commodity page
  3. Acquires the corresponding logistics providers
  4. Please calculate freight logistics providers
  5. Made logistics business name
  6. Freight get results
  7. The name of the freight results are presented to the page

Different parts, is finding ways to narrow down to a minimum, that is: whether this condition, which affects only different thing. The same section, placed outside the predicate. As shown below:

Different parts

Different parts, refers to "choose which among logistics providers on the screen", and this judgment, which affects only a logistics provider object to use. And all of the logistics business objects, are in line with "logistics providers Interface" (either inherited or practice, is the relationship Is-A).

到这边,就只是透过一个 function ,将不同的部分放到参数中,以决定回传哪一个物流商对象。相同的部分,则放到判断式之外,用来描述 context 的流程与商业逻辑。

恭喜您,这就是策略模式。

如 wiki 上的描述:

the strategy pattern (also known as the policy pattern) is a particular software design pattern, whereby algorithms can be selected at runtime.

也就是,在执行阶段时,可以依据不同情况选择不同的算法。

来看一下 wiki 上 strategy pattern 的 class diagram :

在这个例子里,我们的程序若画成 class diagram ,就是按照这样的 pattern 所设计。如下图所示:

strategy pattern class diagram

小结

策略模式,难吗?如果您已经把程序重构成这副模样,相信我,你真的不必懂“策略模式”这四个字。因为我们重构用的就只是最基本的面向对象精神与设计原则。

但,这也不代表着开发人员就不需要了解或学习设计模式。设计模式,就像 UML 一样,除了可以拿来当作特定类型问题的 guidance 蓝图,也很常拿来沟通。当开发人员或分析设计人员,针对某一个情境、需求或问题时,可能只需要用“策略模式”四个字,就可以让每个人心里面有着基本的 class model ,并快速的 mapping 到眼前的情境。

想像一下,以这例子,每个人眼前面对的是重构前的程序,一个人提出:我们可以透过“策略模式”来重构,来把重复的程序降到最低,职责分离,并且对扩充开放,对修改封闭。这时,如果学习并了解过策略模式,大家脑袋里基本上就会把页面放到 context ,把抽象职责相同的部分淬练出一个界面,让每个对象不同的实践细节封装起来,页面只需要透过界面,就能保持一致。

心中无招,就能不被设计模式的框框给设限住。但无招不代表乱七八糟,而是掌握最基本的精神、原则,针对眼前的问题,使用者的需求来解决。

建议

读者朋友可以试试,当碰到一个问题或需求时,先别去寻找哪一个 pattern 适用,而是透过这一系列的方式,先动手重构。直到您觉得重构完成了,接着去看这样的问题,适合用哪一种 pattern ,接着比对您的设计与 GoF 原生的 design pattern ,有何异同。

接着用心去体会,不同的地方,是否属于自己情境或问题下,需客制化或变形的部分。还是单纯设计的冗赘,不够精简、精准。

如果是后者,恭喜你,你趁机学到了自己之前的盲点,再下一次的需求,您就更能使出 pattern 中的精妙之处。

如果是前者,恭喜你,您可以理解在自己的问题领域中,除了最原生的问题解决了,还更弹性地符合了使用者的需求。

去体会个中差异,才能活学活用。设计模式,只是一些常见的问题领域,所衍生出常见的模式解决方式,它是一种最普遍、最抽象、最基础的解决方式,不要去强求自己的设计所产生出来的 class diagram 一定要跟原著或 wiki 上图形一模一样,但绝对要能清楚说出来,为何不一样。

最后,在重构中设计模式的确是一种很方便、快速、好用的手法,但这边要强调的是,开发人员应该要能由需求、问题、 legacy code 当出发点,在重构的过程中,实践并体会出,由原始程序演变成某一种或多种设计模式所搭配设计的最终结果。如此一来,您才真的能体会到设计模式的髓。(因为设计模式的演变过程,绝大部分也正是从重构而来)

当您已经能完全体会且累积了许多相同问题领域的重构手法后,面对这篇文章范例这类的问题,心中无想,就会自然而然的使出策略模式来解决。


最后搞笑一下,下面是大家很熟稔的一段台词:

张三丰:无忌,你有九阳神功护体,学什么武功都特别快,太极拳只重其义,不重其招,你忘记所有招式,就练成太极拳了!

Chi Master: You do not remember?
Zhang Wuji: do not remember!
Chi Master: What's this fist?
Zhang Wuji: I do not know!
Chi Master: What name do you dad?
Zhang Wuji: I forgot!
Chi Master: Good! Just remember the two bastards labeled as useless on the line!

forgotten
(Source: youtube: http://www.youtube.com/watch?v=FaGUA-hUsys)

So basically, object-oriented basic meaning, purpose, spirit and principles, like the side of the nine positive magic, there are nine positive magic body care, learn what pattern faster.

It does not have to memorize what pattern, just remember: you can solve your problems and meet the needs of users on the list!


Perhaps you would be interested in the following training courses:

  1. 2019/7/27 (six) ~ 2019/7/28 (day): Evolution of design: Test-driven development and continuous refactoring sixth echelon (Chinese Taipei)
  2. 2019/8/16 (e) ~ 2019/8/18 (day): [C # Advanced Design - Design learn from the reconstructed high ease of use and high flexibility API] second echelon (Chinese Taipei)
  3. 2019/9/21 (six) ~ 2019/9/22 (day): Clean Coder: DI and AOP advanced combat second echelon (Chinese Taipei)
  4. 2019/10/19 (f): [Art added unit tests for legacy code] seventh echelon (Chinese Taipei)
  5. 2019/10/20 (day): [development] Speed ​​eighth echelon (Chinese Taipei)

Would like to receive first-hand information public training courses, or would like to inquire about house training, consulting, coaching, consulting services, please contact Facebook fan page: 91 agile development of the road.

Original: Large column  [30 days to get started quickly TDD] [Day 17] Refactoring - Strategy Pattern


Guess you like

Origin www.cnblogs.com/petewell/p/11516448.html