2007年2月1日
http://www.cnblogs.com/birdshover/
目录:
一、抽象的产生
二、依赖抽象
三、抽象的损耗
一、抽象的产生
你不知道你要实现的是个什么东西,你能很好的抽象出来么?我不是天才,所以我是不能。如果是那些天才至少也要是白痴天才型的人才能在毫无根据的情况下,假设出真是的存在。尔后居然被伟人证实居然是真的。比如说那个什么××××猜想。
我认为,抽象要么是在建立模型后的产物,要么是重构的结果。而抽象是为了解决程序未来面临的变动。但是你即便把实体抽象出来了,不使用也是没有效果的。而前人总结出经验如何使用这些抽象产物——依赖倒置(Dependence Inversion Principle)。讲的是:要依赖于抽象,不要依赖于具体。也就是经常说的面向接口编程。(这地方的接口是广义上的接口)
图2.1 抽象的产生与运用
假设公司装了一扇自动门,有人来会开,人走了就关上。
1 public class Door
2 {
3 private bool HavaPeson = false; //是否有人,默认没有。自动门,该状态会自动改变。
4
5 public void Open()
6 {
7 if (HavaPeson)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!HavaPeson)
14 Console.WriteLine("Closed");
15 }
16 }
2 {
3 private bool HavaPeson = false; //是否有人,默认没有。自动门,该状态会自动改变。
4
5 public void Open()
6 {
7 if (HavaPeson)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!HavaPeson)
14 Console.WriteLine("Closed");
15 }
16 }
我们把它抽象出来
1 interface IDoor
2 {
3 void Close();
4 void Open();
5 }
2 {
3 void Close();
4 void Open();
5 }
1 public class Door : IDoor
2 {
3 private bool HavaPeson = false;
4
5 public void Open()
6 {
7 if (HavaPeson)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!HavaPeson)
14 Console.WriteLine("Closed");
15 }
16 }
2 {
3 private bool HavaPeson = false;
4
5 public void Open()
6 {
7 if (HavaPeson)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!HavaPeson)
14 Console.WriteLine("Closed");
15 }
16 }
但是这对我们好像并没有用。
这个时候公司又要装一扇门,手推才会开,不推自动关上。
二、依赖抽象
1 public class Door2
2 {
3 private bool IsPush = false;
4
5 public void Open()
6 {
7 if (IsPush)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!IsPush)
14 Console.WriteLine("Closed");
15 }
16 }
2 {
3 private bool IsPush = false;
4
5 public void Open()
6 {
7 if (IsPush)
8 Console.WriteLine("Opend");
9 }
10
11 public void Close()
12 {
13 if (!IsPush)
14 Console.WriteLine("Closed");
15 }
16 }
Door2也实现了IDoor接口啊!
所以改成
public class Door2 : IDoor
{}
老总感觉这两扇门不错,所以要全大楼装上这样的门,并且采用电脑控制。
如果每个门一个控制端,那累也累死了。所以可以实现如下。
图2.2 抽象关系
1 public class ContorlDoor
2 {
3 private IDoor[] door = new IDoor[100];
4 private bool flag = false; //判断门和人的关系状态
5
6 public void CDoor(int i)
7 {
8 door[i] = (IDoor)new Door();
9 Contorl(door[i]);
10 }
11
12 public void CDoor2(int i)
13 {
14 door[i] = (IDoor)new Door2();
15 Contorl(door[i]);
16 }
17
18 public void Contorl(IDoor d)
19 {
20 if (flag)
21 d.Open();
22 else
23 d.Close();
24 }
25 }
2 {
3 private IDoor[] door = new IDoor[100];
4 private bool flag = false; //判断门和人的关系状态
5
6 public void CDoor(int i)
7 {
8 door[i] = (IDoor)new Door();
9 Contorl(door[i]);
10 }
11
12 public void CDoor2(int i)
13 {
14 door[i] = (IDoor)new Door2();
15 Contorl(door[i]);
16 }
17
18 public void Contorl(IDoor d)
19 {
20 if (flag)
21 d.Open();
22 else
23 d.Close();
24 }
25 }
现在即使要加新的门,只要让新型号的门实现接口IDoor就可以了。当然,这地方使用接口而不是抽象类,是根据第一篇文章决定的。探讨继承与实现(一)
至于使用抽象类也是一样的道理。
三、抽象的损耗
所谓有所得必有所失,灵活的架构必然会带来性能上的损耗。
损耗的第一点,大家应该都看出来了,就是类型转换。需要把类的实例转换成接口或者抽象类。而转换除了带来灵活以外,并没有其它的收获。
1 public class Test : ITest
2 {
3
4 #region ITest 成员
5 private int myint;
6 private string s;
7
8 public string otherStr
9 {
10 get
11 {
12 return s;
13 }
14
15 set
16 {
17 s = value;
18 }
19 }
20
21
22
23 public int MyInt
24 {
25 get
26 {
27 return myint;
28 }
29 set
30 {
31 myint = value;
32 }
33 }
34
35 public string MyStr()
36 {
37 return "myStr";
38 }
39
40 #endregion
41
42 public string OtherStr()
43 {
44 return otherStr;
45 }
46 }
2 {
3
4 #region ITest 成员
5 private int myint;
6 private string s;
7
8 public string otherStr
9 {
10 get
11 {
12 return s;
13 }
14
15 set
16 {
17 s = value;
18 }
19 }
20
21
22
23 public int MyInt
24 {
25 get
26 {
27 return myint;
28 }
29 set
30 {
31 myint = value;
32 }
33 }
34
35 public string MyStr()
36 {
37 return "myStr";
38 }
39
40 #endregion
41
42 public string OtherStr()
43 {
44 return otherStr;
45 }
46 }
1 public interface ITest
2 {
3 int MyInt
4 {
5 get;
6 set;
7 }
8
9 string otherStr { get;set;}
10 string MyStr();
11 }
2 {
3 int MyInt
4 {
5 get;
6 set;
7 }
8
9 string otherStr { get;set;}
10 string MyStr();
11 }
1 static void Main(string[] args)
2 {
3 ITest it = (ITest)new Test();
4 Test t = (Test)it;
5 t.otherStr = "1";
6 it.otherStr = "2";
7 Console.WriteLine(t.OtherStr());
8 Console.ReadKey();
9 }
以上代码输出值 22 {
3 ITest it = (ITest)new Test();
4 Test t = (Test)it;
5 t.otherStr = "1";
6 it.otherStr = "2";
7 Console.WriteLine(t.OtherStr());
8 Console.ReadKey();
9 }
实例it不能使用方法OtherStr,而再转化回来后还能使用,证明内存种存在的是Test的实例。并没有因为ITest 没有OtherStr而砍掉。只是再it实例中屏蔽(简化)了我们的访问。
图2.3 类的转换前后
从图中可以看出,转换只是给了我们一个望远镜,一面能看到很大的视野,一面看到的小。就和古人写的管中窥豹是一个道理吧。
而为什么输出的是2而不是1,那是因为类是引用类型。而转换后还是引用类型。it和t看似两个实例,实际上指向的是同一段内存地址,只是留给你的看台不一样。