您的位置:首页 > 其它

设计模式(7):模板方法模式

2015-02-05 12:17 363 查看
模板方法模式是很常用的方法,对继承和多态玩的好的人几乎都会在继承体系中多多少少的用到它。比如在 .NET 或 Java 类库的设计中,通常都会用模板方法模式提取类库中的公共行为到抽象类中。

之所以会有设计模式一说,我认为就是为了减少代码重复量,尽可能降低后续更改成本,提高代码运行效率,模板方法模式也不例外。

本文引用一个小例子(考试试卷)深入浅出介绍模板方法模式。

学生A、B同时抄写科目一试题并给出自己的答案:

1: //学生A抄的试卷类

2: class TestPaperA

3: {

4:     public void TestQuestion1()

5:     {

6:         Console.WriteLine("驾驶人有下列哪种违法行为一次记12分");

7:         Console.WriteLine("A.违反交通信号灯 B.使用伪造机动车号牌 C.违反禁令标志指示 D.拨打、接听手机的");

8:         Console.WriteLine("答案:" + "B");

9:         Console.WriteLine();

10:     }

11:

12:     public void TestQuestion2()

13:     {

14:         Console.WriteLine("怎样操作才能防止汽车在冰雪路面上起步打滑");

15:         Console .WriteLine ("A.比起步档高一级的档位起步 B.比起步档低一级的档位起步 C.起步档 D.比起步档高二级的档位起步");

16:         Console.WriteLine("答案:" + "A");

17:         Console.WriteLine();

18:     }

19:

20:     public void TestQuestion3()

21:     {

22:         Console.WriteLine("驾驶人连续行驶不得超过多少时间");

23:         Console.WriteLine("A.10小时 B.8小时 C.6小时 D.4小时");

24:         Console.WriteLine("答案:" + "D");

25:         Console.WriteLine();

26:     }

27: }

28:

29: //学生B抄的试卷类

30: class TestPaperB

31: {

32:     public void TestQuestion1()

33:     {

34:         Console.WriteLine("驾驶人有下列哪种违法行为一次记12分");

35:         Console.WriteLine("A.违反交通信号灯 B.使用伪造机动车号牌 C.违反禁令标志指示 D.拨打、接听手机的");

36:         Console.WriteLine("答案:" + "C");

37:         Console.WriteLine();

38:     }

39:

40:     public void TestQuestion2()

41:     {

42:         Console.WriteLine("怎样操作才能防止汽车在冰雪路面上起步打滑");

43:         Console.WriteLine("A.比起步档高一级的档位起步 B.比起步档低一级的档位起步 C.起步档 D.比起步档高二级的档位起步");

44:         Console.WriteLine("答案:" + "A");

45:         Console.WriteLine();

46:     }

47:

48:     public void TestQuestion3()

49:     {

50:         Console.WriteLine("驾驶人连续行驶不得超过多少时间");

51:         Console.WriteLine("A.10小时 B.8小时 C.6小时 D.4小时");

52:         Console.WriteLine("答案:" + "B");

53:         Console.WriteLine();

54:     }

55: }




可以看出学生A、B都要把题目都抄写一遍,客户端代码:

1: static void Main(string[] args)

2: {

3:     Console.WriteLine("学生A抄的试卷类:");

4:     TestPaperA studentA = new TestPaperA();

5:     studentA.TestQuestion1();

6:     studentA.TestQuestion2();

7:     studentA.TestQuestion3();

8:

9:     Console.WriteLine("学生B抄的试卷类:");

10:     TestPaperB studentB = new TestPaperB();

11:   studentB.TestQuestion1();

12:     studentB.TestQuestion2();

13:     studentB.TestQuestion3();

14:

15:     Console.Read();

16: }




运行结果:







不难看出,学生A、B抄的题目一样,只是给出的答案不同而已,这样写既容易错,又难维护。容易想到这里应该把试题和答案共享,抽象出父类,让两个子类继承于它,公共的题目代码写到父类当中,看看更改之后的代码:

抽象出父类,包含公共题目:

1: class TestPaper

2: {

3:     public void TestQuestion1()

4:     {

5:         Console.WriteLine("驾驶人有下列哪种违法行为一次记12分");

6:         Console.WriteLine("A.违反交通信号灯 B.使用伪造机动车号牌 C.违反禁令标志指示 D.拨打、接听手机的");

7:     }

8:

9:     public void TestQuestion2()

10:     {

11:       Console.WriteLine("怎样操作才能防止汽车在冰雪路面上起步打滑");

12:         Console.WriteLine("A.比起步档高一级的档位起步 B.比起步档低一级的档位起步 C.起步档 D.比起步档高二级的档位起步");

13:     }

14:

15:     public void TestQuestion3()

16:     {

17:         Console.WriteLine("驾驶人连续行驶不得超过多少时间");

18:         Console.WriteLine("A.10小时 B.8小时 C.6小时 D.4小时");

19:   }

20: }




学生A、B抄的试卷:

1: //学生A抄的试卷类

2: class TestPaperA : TestPaper

3: {

4:     public new void TestQuestion1()

5:     {

6:         base.TestQuestion1();

7:         Console.WriteLine("答案:" + "B");

8:       Console.WriteLine();

9:     }

10:

11:   public new void TestQuestion2()

12:     {

13:         base.TestQuestion2();

14:       Console.WriteLine("答案:" + "A");

15:         Console.WriteLine();

16:     }

17:

18:     public new void TestQuestion3()

19:   {

20:         base.TestQuestion3();

21:         Console.WriteLine("答案:" + "D");

22:         Console.WriteLine();

23:     }

24: }

25:

26: //学生B抄的试卷类

27: class TestPaperB : TestPaper

28: {

29:     public new void TestQuestion1()

30:     {

31:         base.TestQuestion1();

32:         Console.WriteLine("答案:" + "C");

33:         Console.WriteLine();

34:     }

35:

36:     public new void TestQuestion2()

37:     {

38:         base.TestQuestion2();

39:       Console.WriteLine("答案:" + "A");

40:         Console.WriteLine();

41:     }

42:

43:     public new void TestQuestion3()

44:     {

45:         base.TestQuestion3();

46:         Console.WriteLine("答案:" + "B");

47:       Console.WriteLine();

48:     }

49: }




更改之后只需要填写答案就可以了,但这还只是初步的泛化,两个学生类中还是有相似的代码,比如“base.TestQuestion”,“Console.WriteLine(“答案:")”,除了选项不同,其他都是重复的。我们既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该上升到父类中去,而不是让每个子类都去重复。

现在模板方法模式登场,当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑使用模板方法模式。更改方法是将不同的细节部分用虚方法代替:

1: class TestPaper

2: {

3:     public void TestQuestion1()

4:     {

5:         Console.WriteLine("驾驶人有下列哪种违法行为一次记12分");

6:         Console.WriteLine("A.违反交通信号灯 B.使用伪造机动车号牌 C.违反禁令标志指示 D.拨打、接听手机的");

7:         Console.WriteLine("答案:" + Answer1());

8:       Console.WriteLine();

9:     }

10:

11:   public void TestQuestion2()

12:     {

13:         Console.WriteLine("怎样操作才能防止汽车在冰雪路面上起步打滑");

14:       Console.WriteLine("A.比起步档高一级的档位起步 B.比起步档低一级的档位起步 C.起步档 D.比起步档高二级的档位起步");

15:         Console.WriteLine("答案:" + Answer2());

16:         Console.WriteLine();

17:   }

18:

19:   public void TestQuestion3()

20:     {

21:         Console.WriteLine("驾驶人连续行驶不得超过多少时间");

22:         Console.WriteLine("A.10小时 B.8小时 C.6小时 D.4小时");

23:         Console.WriteLine("答案:" + Answer3());

24:         Console.WriteLine();

25:   }

26:

27:     protected virtual string Answer1()

28:   {

29:         return "";

30:     }

31:

32:     protected virtual string Answer2()

33:     {

34:         return "";

35:   }

36:

37:     protected virtual string Answer3()

38:     {

39:       return "";

40:     }

41: }






1: //学生A抄的试卷类

2: class TestPaperA : TestPaper

3: {

4:     protected override string Answer1()

5:     {

6:           return "B";

7:     }

8:

9:     protected override string Answer2()

10:     {

11:       return "A";

12:     }

13:

14:   protected override string Answer3()

15:     {

16:         return "D";

17:   }

18: }

19:

20: //学生B抄的试卷类

21: class TestPaperB : TestPaper

22: {

23:     protected override string Answer1()

24:     {

25:       return "C";

26:     }

27:

28:   protected override string Answer2()

29:     {

30:         return "A";

31:   }

32:

33:     protected override string Answer3()

34:     {

35:       return "B";

36:   }

37: }




客户端稍微更改,本来是子类变量的声明,改成了父类,这样就可以利用多态性实现代码的复用了:

1: static void Main(string[] args)

2: {

3:     TestPaper student;

4:     Console.WriteLine("学生A抄的试卷类:");

5:     student = new TestPaperA();

6:     student.TestQuestion1();

7:     student.TestQuestion2();

8:   student.TestQuestion3();

9:

10:   Console.WriteLine("学生B抄的试卷类:");

11:   student = new TestPaperB();

12:     student.TestQuestion1();

13:   student.TestQuestion2();

14:   student.TestQuestion3();

15:

16:     Console.Read();

17: }




以上就是典型的模板方法模式了,其实在实际中还是比较常用的。模板方法模式定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法模式结构图:







AbstractClass 是抽象类,也就是抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也可能调用一些具体方法。

1: abstract class AbstractClass

2: {

3:     public abstract void PrimitiveOperation1();

4:     public abstract void PrimitiveOperation2();

5:

6:     public void TemplateMethod()

7:     {

8:       PrimitiveOperation1();

9:       PrimitiveOperation2();

10:       Console.WriteLine("");

11:   }

12: }




ConcreteClass ,实现父类所定义的一个或多个抽象方法。每一个 AbstractClass 类都可以有任意多个 ConcreteClass 与之对应,而每一个 ConcreteClass 都可以给出这些抽象方法的不同实现,从而使得顶级逻辑的实现各不相同。

1: class ConcreteClassA : AbstractClass

2: {

3:     public override void PrimitiveOperation1()

4:     {

5:       Console.WriteLine("具体类A方法1实现");

6:     }

7:     public override void PrimitiveOperation2()

8:   {

9:       Console.WriteLine("具体类A方法2实现");

10:     }

11: }

12:

13: class ConcreteClassB : AbstractClass

14: {

15:   public override void PrimitiveOperation1()

16:     {

17:       Console.WriteLine("具体类B方法1实现");

18:     }

19:   public override void PrimitiveOperation2()

20:     {

21:         Console.WriteLine("具体类B方法2实现");

22:     }

23: }




客户端:

1: static void Main(string[] args)

2: {

3:     AbstractClass c;

4:

5:   c = new ConcreteClassA();

6:     c.TemplateMethod();

7:

8:   c = new ConcreteClassB();

9:   c.TemplateMethod();

10:

11:   Console.Read();

12:

13: }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: