编写程序遇到多分支的情况总是少不了 switch/if 语句。在面向对象编程里大部分的 switch 语句都是可以避免的。使用多态简单工厂模式可以消除多余的 switch 语句。

简单的需求

编写程序根据序号输出动物的名字和叫声。

序号 名字 叫声
1 猫咪 喵喵喵?
2 青蛙 蛤?

过程式的实现方式

思路

直接从输入读入数字,用 switch 判断并输出名字和叫声。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Program
{
static void Main(string[] args)
{
Console.Write("请输入序号:");
string no = Console.ReadLine();

switch (no)
{
case "1":
Console.WriteLine("名为猫咪");
Console.WriteLine("叫了一声:喵喵喵?");
break;

case "2":
Console.WriteLine("名为青蛙");
Console.WriteLine("叫了一声:蛤?");
break;

default:
Console.WriteLine("这种情况是要加钱的!");
break;
}

Console.Read();
}
}

解析

使用了传统的过程式的编程思想。没有多余的 switch 语句。本文完。。。。。。个屁嘞。实际情况中很可能不需要同时输出名字和叫声。所以需要将相关的逻辑封装。

封装的实现方式

思路

将相关的逻辑代码封装到函数中。并将这两个函数封装到一个系统类中。需要时调用即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Program
{
static void Main(string[] args)
{
Console.Write("请输入序号:");
string no = Console.ReadLine();

// 具体实现移到 AnimalSystem
Console.WriteLine(AnimalSystem.Name(no));
Console.WriteLine(AnimalSystem.Sound(no));

Console.Read();
}
}

class AnimalSystem
{
public static string Name(string no)
{
switch (no)
{
case "1":
return "名为猫咪";
break;
case "2":
return "名为青蛙";
break;
default:
return "这种情况是要加钱的!";
break;
}
}

public static string Sound(string no)
{
switch (no)
{
case "1":
return "叫了一声:喵喵喵?";
break;
case "2":
return "叫了一声:蛤?";
break;
default:
return "这种情况是要加钱的!";
break;
}
}
}

解析

名字和叫声的逻辑都封装到 AnimalSystem 中了。但还不够面向对象。

面向对象的实现方式

思路

新建猫咪青蛙类,继承于动物类。将名字和叫声移到相应的类中。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class Program
{
static void Main(string[] args)
{
Console.Write("请输入序号:");
string no = Console.ReadLine();

Console.WriteLine(AnimalSystem.Name(no));
Console.WriteLine(AnimalSystem.Sound(no));

Console.Read();
}
}

class AnimalSystem
{
// 引入对象
public static string Name(string no)
{
// 第 1 个 switch
switch (no)
{
case "1":
return new Cat().Name();
break;
case "2":
return new Frog().Name();
break;
default:
return new Animal().Name();
break;
}
}

// 引入对象
public static string Sound(string no)
{
// 第 2 个 switch
switch (no)
{
case "1":
return new Cat().Sound();
break;
case "2":
return new Frog().Sound();
break;
default:
return new Animal().Sound();
break;
}
}
}

class Animal
{
public virtual string Name() { return "这种情况是要加钱的!"; }
public virtual string Sound() { return "这种情况是要加钱的!"; }
}

class Cat : Animal
{
public override string Name()
{
return "名为猫咪";
}

public override string Sound()
{
return "叫了一声:喵喵喵?";
}
}

class Frog : Animal
{
public override string Name()
{
return "名为青蛙";
}

public override string Sound()
{
return "叫了一声:蛤?";
}
}

解析

现在的代码已经足够面向对象了。但是代码中出现了两次 switch 语句。过程式的代码可是只有一次。有没有什么办法既使用面向对象又只使用一次 switch 语句呢?答案是肯定的。使用简单工程模式搭配多态可以做到。

简单工厂的实现方式

思路

两次 switch 语句出现在 序号→名字 和 序号→叫声 中。先使用简单工厂模式产生 序号→类型 的转换,这时需要一次 switch 语句。之后利用面向对象的多台性质产生 类型→名字 和 类型→叫声 这两次转换。这次转换不需要 switch 语句。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class Program
{
static void Main(string[] args)
{
Console.Write("请输入序号:");
string no = Console.ReadLine();

// 序号 → 类型
Animal animal = AnimalFactory.GetAnimal(no);
Console.WriteLine(AnimalSystem.Name(animal));
Console.WriteLine(AnimalSystem.Sound(animal));

Console.Read();
}
}

class AnimalSystem
{
public static string Name(Animal animal)
{
// 类型 → 名字
return animal.Name();
}

public static string Sound(Animal animal)
{
// 类型 → 声音
return animal.Sound();
}
}

class Animal
{
public virtual string Name() { return "这种情况是要加钱的!"; }
public virtual string Sound() { return "这种情况是要加钱的!"; }
}

class Cat : Animal
{
public override string Name()
{
return "名为猫咪";
}

public override string Sound()
{
return "叫了一声:喵喵喵?";
}
}

class Frog : Animal
{
public override string Name()
{
return "名为青蛙";
}

public override string Sound()
{
return "叫了一声:蛤?";
}
}

class AnimalFactory
{
public static Animal GetAnimal(string no)
{
// switch 语句移到这里。唯一一次 switch
switch (no)
{
case "1":
return new Cat();
break;
case "2":
return new Frog();
break;
default:
return new Animal();
break;
}
}
}

解析

通过引入 AnimalFactory 这个简单工厂,之后通过多态特性消除了多次的 switch 语句。如果需要添加新的类型如美羊羊,则需要修改3个地方。

  1. 新建类继承自 Animal
  2. 实现相应的 Name()Sound() 方法
  3. 在工厂类 AnimalFactory 中添加新的 case

实际上就算是不了解简单工程模式也很可能写出来。设计模式应该是自然而然的应用而不是刻意地使用,产生冗余的代码。

总结

这个简单的例子还有很多可以优化的地方。例如 Animal 类应该是抽象类,不包含具体的实现。同时可以引入Null Class来用于输出 这种情况是要加钱的!等类似没有实现的情况下的默认行为。还可以将简单工厂模式改成工厂模式以符合开放-封闭原则。不过这些都不是本文范围内的。我学膜法去了。 :)