面向对象编程(一)
间接,最先看到这个词是在<<Objective-C基础教程>>中,被称为面向对象的基础,那什么是间接呢? 首先我们看一个小程序,就一句话,求长方形的面积:
1: static void Main(string[] args)
2: {
3: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", 6, 3, 6 * 3);
4: }
这段程序非常简单,也没有错误,那么有什么问题呢?
1.这个程序只能计算长为:6,宽为3的的长方形面积.(如果只是这样,那么一点问题都没有,但是我们以后也许会在很多时候需要求各种长方形的面积);
所以这时我们如果需要改为求长为:10,宽为4的长方形面积,那么这段程序完全失去了作用,所以我们改写程序:
1: static void Main(string[] args)
2: {
3: int a = 10;
4: int b = 4;
5: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a,b,a*b);
6: }
这里我们把长宽都换成了变量,如果我们需要计算别的长宽不同的长方形面积,只需要修改变量的值就可以了.
这里我们就用到了间接(没有直接使用10,4,这种常量,而是把它们赋值于两个变量,a,b);
但是它还有缺点,如果我们要计算输出好几个长方形的面积,那么我们只能这样写:
1: static void Main(string[] args)
2: {
3: int a = 10;
4: int b = 4;
5: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a,b,a*b);
6:
7: a = 20;
8: b = 6;
9: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
10:
11: //......
12: //......
13: }
这样就看出来非常麻烦了,只要我们以后稍微有些变化,比如现在要为这句话”长为:{0},宽为:{1}的长方形面积为:{2}”的最后加一个叹号,那么我们需要全应用程序的查找这句话被用到的地方,然后一个一个去添加,显然是非常麻烦的,所以我们改写为:
1: static void Main(string[] args)
2: {
3: int a = 10;
4: int b = 4;
5: CalculatingArea(a, b);
6:
7: a = 20;
8: b = 6;
9: CalculatingArea(a, b);
10:
11: //......
12: //......
13: }
14:
15: private static void CalculatingArea(int a, int b)
16: {
17: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
18: }
这样我们不直接使用”Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);” 这句计算面积的语句,而是把它写入一个函数,这样间接的使用,会给我们带来很大的方便,现在如果再修改,那么我们只需要修改一个地方就可以了.
显然到现在这个程序还有问题,如果多个不同的类,需要去调用这个方法,比如有添加了一个Test类,这个类里也需要这个方法:
1: public class Test
2: {
3: public void TestCalculate()
4: {
5: int a = 10;
6: int b = 5;
7: this.CalculatingArea(a, b);
8: }
9:
10: private void CalculatingArea(int a, int b)
11: {
12: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
13: }
14: }
这样就又回到了开始,如果需要修改一下输出代码,那么我们需要找到有这段代码的所有的类,然后去修改,这时我们可以在做一次间接,不是直接调用这个函数,而是把它单独放入一个类中,修改后代码为:
1: public class Calculate
2: {
3: public void CalculatingArea(int a, int b)
4: {
5: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
6: }
7: }
8: public class Test
9: {
10: public void TestCalculate()
11: {
12: Calculate calculate = new Calculate();
13: int a = 10;
14: int b = 5;
15: calculate.CalculatingArea(a, b);
16: }
17: }
18: class Program
19: {
20: static void Main(string[] args)
21: {
22: Calculate calculate = new Calculate();
23:
24: int a = 10;
25: int b = 4;
26: calculate.CalculatingArea(a, b);
27:
28: a = 20;
29: b = 6;
30: calculate.CalculatingArea(a, b);
31:
32: //......
33: //......
34: }
35: }
这样我们提取出来了一个类,所有计算长方形面积都调用Calculate这个类.如果遇到修改,只修改这个类就行了.又一次间接调用,而不是直接.
这段代码还需要修改什么吗? 如果我们添加一个三角形的面积计算,我们怎么做呢?假如我们按照计算长方形的面积的方式添加一个计算三角形的面积的类:
1: public class CalculateTriangle
2: {
3: public void CalculatingArea(int a, int b)
4: {
5: Console.WriteLine("底为:{0},高为:{1}的三角形面积为:{2}", a, b, a * b*0.5);
6: }
7: }
这样看起来应该是没什么问题了,可是如果需求又变了,要求接受用户输入,首先用户会输入1或者2,1代表我们要计算出长方形的面积,2代表我们要计算出三角形的面积,然后用户输入两个值,我们根据用户的输入进行计算,这时的代码:
1: static void Main(string[] args)
2: {
3: int switchKey = 0;
4: switchKey=int.Parse(Console.ReadLine());
5: int a = 0;
6: int b = 0;
7: a=int.Parse(Console.ReadLine());
8: b = int.Parse(Console.ReadLine());
9: if (switchKey == 1)
10: {
11: CalculateRectangle calculate = new CalculateRectangle();
12: calculate.CalculatingArea(a, b);
13: }
14: if (switchKey == 2)
15: {
16: CalculateTriangle calculate = new CalculateTriangle();
17: calculate.CalculatingArea(a, b);
18: }
19: }
效果为:
这样的做法有什么问题呢? 如果我们需要再添加一个圆形面积的计算,我们怎么办呢? 继续这样添加?还是考虑别的方法呢? 显然,如果我们做的是求所有图形的面积的程序,那么我们还有N多图形没有弄进来,如果用户想到一个,我们添加一个,那么我们会不断的修改程序,直到无法维护.这时怎么办呢? 还是按照我们上边改进长方形面积的计算的方法一步一步来,首先这个a,b变量,放到main函数里获取是否合适呢? 如果我们计算圆形的,那么我们只需要知道半径就行了,用户只用输入一个值,那么我们现在的程序就不满足了,怎么办,计算每个图形,需要用户输入的东西不一样,那么我们是不是把他们放入计算的函数里,而不是在main里呢?修改,添加圆形后代码为:
1: public class CalculateCircle
2: {
3: public void CalculatingArea()
4: {
5: Double r = 0;
6: r = Double.Parse(Console.ReadLine());
7: Console.WriteLine("半径为:{0}的圆形面积为:{1}", r, 3.14*r*r);
8: }
9: }
10: public class CalculateRectangle
11: {
12: public void CalculatingArea()
13: {
14: int a = 0;
15: int b = 0;
16: a = int.Parse(Console.ReadLine());
17: b = int.Parse(Console.ReadLine());
18: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
19: }
20: }
21: public class CalculateTriangle
22: {
23: public void CalculatingArea()
24: {
25: int a = 0;
26: int b = 0;
27: a = int.Parse(Console.ReadLine());
28: b = int.Parse(Console.ReadLine());
29: Console.WriteLine("底为:{0},高为:{1}的三角形面积为:{2}", a, b, a * b*0.5);
30: }
31: }
32: class Program
33: {
34: static void Main(string[] args)
35: {
36: int switchKey = 0;
37: switchKey=int.Parse(Console.ReadLine());
38: if (switchKey == 1)
39: {
40: CalculateRectangle calculate = new CalculateRectangle();
41: calculate.CalculatingArea();
42: }
43: if (switchKey == 2)
44: {
45: CalculateTriangle calculate = new CalculateTriangle();
46: calculate.CalculatingArea();
47: }
48: if (switchKey == 3)
49: {
50: CalculateCircle calculate = new CalculateCircle();
51: calculate.CalculatingArea();
52: }
53: }
54: }
这时我们还能怎么进步呢? 大家可以观察Main函数里的代码,这里不管实例化那个类,最后都是调用的”calculate.CalculatingArea();”这句代码,但他们仅仅只是基于巧合,也就是正好儿名字一样了,但这个”正好儿”我们能把他弄成是”必须得”吗? 我们可不可以定义一个约定,你的所有的计算类都必须符合我的约定,必须有
“CalculatingArea()”方法呢? 这样我们就用到了接口,代码为:
1: public interface ICalculate
2: {
3: void CalculatingArea();
4: }
5: public class CalculateCircle : ICalculate
6: {
7: public void CalculatingArea()
8: {
9: Double r = 0;
10: r = Double.Parse(Console.ReadLine());
11: Console.WriteLine("半径为:{0}的圆形面积为:{1}", r, 3.14*r*r);
12: }
13: }
14: public class CalculateRectangle : ICalculate
15: {
16: public void CalculatingArea()
17: {
18: int a = 0;
19: int b = 0;
20: a = int.Parse(Console.ReadLine());
21: b = int.Parse(Console.ReadLine());
22: Console.WriteLine("长为:{0},宽为:{1}的长方形面积为:{2}", a, b, a * b);
23: }
24: }
25: public class CalculateTriangle : ICalculate
26: {
27: public void CalculatingArea()
28: {
29: int a = 0;
30: int b = 0;
31: a = int.Parse(Console.ReadLine());
32: b = int.Parse(Console.ReadLine());
33: Console.WriteLine("底为:{0},高为:{1}的三角形面积为:{2}", a, b, a * b*0.5);
34: }
35: }
36: class Program
37: {
38: static void Main(string[] args)
39: {
40: int switchKey = 0;
41: switchKey=int.Parse(Console.ReadLine());
42: ICalculate calculate=null;
43: if (switchKey == 1)
44: {
45: calculate = new CalculateRectangle();
46: }
47: if (switchKey == 2)
48: {
49: calculate = new CalculateTriangle();
50: }
51: if (switchKey == 3)
52: {
53: calculate = new CalculateCircle();
54: }
55: calculate.CalculatingArea();
56: }
57: }
这样看上去接口定义的变量的作用就和整形定义的变量一样了,只要定义了接口,其他的实现了此接口的类就能当做值付给这个接口所定义的变量了(O(∩_∩)O~),这样代码又整洁了一些了.但是Main函数里,写这么多判断又显得啰嗦了,还是以前的原因,如果多个地方需要这样的判断那么我们就得重复拷贝这份代码,怎么办? 还是把它弄出来:
1: public class CalculateFactory
2: {
3: public static ICalculate GetCalculate()
4: {
5: int switchKey = 0;
6: switchKey = int.Parse(Console.ReadLine());
7: ICalculate calculate = null;
8: if (switchKey == 1)
9: {
10: calculate = new CalculateRectangle();
11: }
12: if (switchKey == 2)
13: {
14: calculate = new CalculateTriangle();
15: }
16: if (switchKey == 3)
17: {
18: calculate = new CalculateCircle();
19: }
20: return calculate;
21: }
22: }
23: class Program
24: {
25: static void Main(string[] args)
26: {
27: ICalculate calculate = CalculateFactory.GetCalculate();
28: calculate.CalculatingArea();
29: }
30: }
这里添加了CalculateFactory类,用来专门实例化得到一个实现了ICalculate接口的类.然后修改了Main函数,其他类没有改变.
那么现在如果我们还需要添加图形面积计算,只需要添加一个实现了ICalculate接口的类,然后在修改CalculateFactory类就行了.
待续……