下面,我们的学习将从咖啡和茶的制作上开始进行。
泡咖啡的步骤:

  • 把水煮沸
  • 用沸水冲泡咖啡
  • 把咖啡倒入杯子
  • 加糖和牛奶

泡茶的步骤:

  • 把水煮沸
  • 用沸水侵泡茶叶
  • 把茶倒入杯子
  • 加柠檬

下面,用代码来实现上面的步骤:
咖啡的实现:

  1. public class Coffee
  2. {
  3. void prepareRecipe()
  4. {
  5. boilWater();
  6. brewCoffeeGrinds();
  7. pourInCup();
  8. addSugarAndMilk();
  9. }
  10. public void boilWater()
  11. {
  12. Console.WriteLine("Boiling water");
  13. }
  14. public void brewCoffeeGrinds()
  15. {
  16. Console.WriteLine("Dripping Coffee through filter");
  17. }
  18. public void pourInCup()
  19. {
  20. Console.WriteLine("Pouring into cup");
  21. }
  22. public void addSugarAndMilk()
  23. {
  24. Console.WriteLine("Adding Sugar and milk");
  25. }
  26. }

茶的实现:

  1. public class Tea
  2. {
  3. void prepareRecipe()
  4. {
  5. boilWater();
  6. brewTeaBag();
  7. addLemon();
  8. pourInCup();
  9. }
  10. public void boilWater()
  11. {
  12. Console.WriteLine("Boiling water");
  13. }
  14. public void brewTeaBag()
  15. {
  16. Console.WriteLine("Steeping the tea");
  17. }
  18. public void pourInCup()
  19. {
  20. Console.WriteLine("pouring into cup");
  21. }
  22. public void addLemon()
  23. {
  24. Console.WriteLine("Adding Lemon");
  25. }
  26. }

从上面的代码可以发现,有两个步骤我们是重复了的,我们可以把重复的部分抽取出来,放入到一个基类中。
把boilWater()、pourInCup()提取出来放入基类,把prepareRecipe()定义成抽象方法。但这样定义,对于prepareRecipe()仍然需要实现两次,我们将boilWater()、pourInCup()泛化,使用brew()和addCondiments()来替代,并且在基类中,将其定义成抽象方法。这样prepareRecipe()不在抽象,而是在基类中实现。
基类:

  1. public abstract class CaffeineBeverage
  2. {
  3. public void prepareRecipe()
  4. {
  5. boilWater();
  6. brew();
  7. addCondiments();
  8. pourInCup();
  9. }
  10. public void boilWater()
  11. {
  12. Console.WriteLine("Boiling water");
  13. }
  14. public void pourInCup()
  15. {
  16. Console.WriteLine("pouring into cup");
  17. }
  18. public abstract void brew();
  19. public abstract void addCondiments();
  20. }

下面看一幅图,来看看我们现在都做了些什么:

模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现
在上面的代码中,我们模板方法就是prepareRecipe(),原因:

  • 它是一个方法
  • 它用作一个算法模板,在这个例子中,算法是用来制作咖啡因饮料
  • 在这里模板中,算法内的每一个步骤都被一个方法代表了。

模板方法模式:在一个方法定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤

这个抽象类包含了模板方法。
primitiveOperation1()、primitiveOperation2()模板方法所用到的操作的抽象版本,模板方法本身和这两个操作的具体实现之间被解耦。
一个模板方法中可能许多具体类,这个具体类实现了抽象类的操作。

钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类决定。
下面来看看代码怎么写:

  1. public abstract class CaffeineBeverageWithHook
  2. {
  3. void prepareRecipe()
  4. {
  5. boilWater();
  6. brew();
  7. poueInCup();
  8. if (customerWantsCondiments())
  9. {
  10. addCondiments();
  11. }
  12. }
  13. public abstract void brew();
  14. public abstract void addCondiments();
  15. void boilWater()
  16. {
  17. Console.WriteLine("Boiling water");
  18. }
  19. void poueInCup()
  20. {
  21. Console.WriteLine("Pouring into cup");
  22. }
  23. bool customerWantsCondiments()//这就是一个钩子,子类可以覆盖(这个方法通常是空的实现)
  24. {
  25. return true;
  26. }
  27. }

让我们看看它在实际代码里面的应用
子类继承:

  1. public class CoffeeWithHook : CaffeineBeverageWithHook
  2. {
  3. public override void addCondiments()
  4. {
  5. Console.WriteLine("Adding Sugar through filter");
  6. }
  7. public override void brew()
  8. {
  9. Console.WriteLine("Dripping Coffee through filter");
  10. }
  11. public override bool customerWantsCondiments()
  12. {
  13. Console.WriteLine("Adding Sugar through filter?Y/N");
  14. string s = Console.ReadLine();
  15. if (s=="y")
  16. {
  17. return true;
  18. }
  19. return false;
  20. }
  21. }

测试:

  1. static void Main(string[] args)
  2. {
  3. CoffeeWithHook coffeewithhool=new CoffeeWithHook();
  4. coffeewithhool.prepareRecipe();
  5. Console.ReadKey();
  6. }

当算法的某个实现可选的时候,可以使用钩子。当算法的某个实现是必须的时候,使用抽象方法

钩子可以让子类有能力为其基类做一些决定。

好莱坞原则:别调用我们,我们会调用你。
好莱坞原则防止依赖腐败:当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件又依赖边测组件,边侧组件又依赖高层组件,这样依赖腐败就发生了。
在好莱坞原则下,我们允许底层组件将自己挂钩到系统上,但高层组件会决定什么时候调用这些底层组件。

我们在之前设计模板方法的时候,其实就用到了好莱坞原则:

在上面图中,CaffeineBeverage就是我们的高层组件,它能够控制冲泡方法的算法,只有在需要子类实现某个方法是才调用,饮料的客户代码只依赖CaffeineBeverage的抽象,而不依赖具体的类。

在这里我们看到好莱坞原则感觉和依赖倒置原则很像,都是用于解耦。
依赖倒置原则让我们尽量避免使用具体类,而多使用抽象,它更加注重与在设计中避免依赖。
好莱坞原则则是一种用在创建框架或组件上的一种技巧,好让底层组件能够被挂钩计算,而又不会让高层组件依赖底层组件,它是创建一个有弹性的设计,允许底层结构能够互相操作,而又防止太过于依赖。

使鸭子类继承至IComparable接口,之后使用sort方法进行排序

  1. public class Duck:IComparable
  2. {
  3. private string name;
  4. private int weight;
  5. public Duck(string name, int weight)
  6. {
  7. this.name = name;
  8. this.weight = weight;
  9. }
  10. public string toString()
  11. {
  12. return name + "weighs" + weight;
  13. }
  14. public int CompareTo(object obj)//需要提供的实现
  15. {
  16. Duck otherDuck = (Duck) obj;
  17. if (this.weight<otherDuck.weight)
  18. {
  19. return -1;
  20. }
  21. else if (this.weight==otherDuck.weight)
  22. {
  23. return 0;
  24. }
  25. else
  26. {
  27. return 1;
  28. }
  29. }
  30. }

排序

  1. static void Main(string[] args)
  2. {
  3. List<Duck> ducks=new List<Duck>{new Duck("11",11),new Duck("2",2),new Duck("13",13),new Duck("7",7)};
  4. DisPlay(ducks);
  5. ducks.Sort();
  6. DisPlay(ducks);
  7. Console.ReadKey();
  8. }

在上面的例子中,我们可以看到子类提供的实现排序方法的算法。

posted on 2018-01-31 09:47 目不识丁 阅读() 评论() 编辑 收藏

版权声明:本文为Tan-sir原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Tan-sir/p/8310899.html