面向接口编程详解(二)——编程实例

本系列《面向接口编程详解》将分为三部分:
面向接口编程详解(一)——思想基础(已发布)
在这一篇中,将对接口及面向接口编程有个大致的介绍,着重在于思想上的讲解。
面向接口编程详解(二)——编程实例(已发布)
这一篇将结合一个实例“移动存储设备模拟”来让大家对面向接口编程有个直观印象。
面向接口编程详解(三)——模式研究(已发布)
讲解几个设计模式中的面向接口思想和基于.NET平台的分层架构中的面向接口思想,加深理解。

通过上一篇文章的讨论,我想各位朋友对“面接接口编程”有了一个大致的了解。那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象。为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅。

问题的提出 


定义:现在我们要开发一个应用,模拟移动存储设备的读写,即计算机与U盘、MP3、移动硬盘等设备进行数据交换。

上下文(环境):已知要实现U盘、MP3播放器、移动硬盘三种移动存储设备,要求计算机能同这三种设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。各个存储设备间读、写的实现方法不同,U盘和移动硬盘只有这两个方法,MP3Player还有一个PlayMusic方法。

名词定义:数据交换={读,写}

看到上面的问题,我想各位脑子中一定有了不少想法,这是个很好解决的问题,很多方案都能达到效果。下面,我列举几个典型的方案。

解决方案列举


方案一:分别定义FlashDisk、MP3Player、MobileHardDisk三个类,实现各自的Read和Write方法。然后在Computer类中实例化上述三个类,为每个类分别写读、写方法。例如,为FlashDisk写ReadFromFlashDisk、WriteToFlashDisk两个方法。总共六个方法。

方案二:定义抽象类MobileStorage,在里面写虚方法Read和Write,三个存储设备继承此抽象类,并重写Read和Write方法。Computer类中包含一个类型为MobileStorage的成员变量,并为其编写get/set器,这样Computer中只需要两个方法:ReadData和WriteData,并通过多态性实现不同移动设备的读写。

方案三:与方案二基本相同,只是不定义抽象类,而是定义接口IMobileStorage,移动存储器类实现此接口。Computer中通过依赖接口IMobileStorage实现多态性。

方案四:定义接口IReadable和IWritable,两个接口分别只包含Read和Write,然后定义接口IMobileStorage接口继承自IReadable和IWritable,剩下的实现与方案三相同。

下面,我们来分析一下以上四种方案:

首先,方案一最直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。或者说,不符合“开放-关闭原则”(注:意为对扩展开放,对修改关闭)。当将来有了第三方扩展移动存储设备时,必须对Computer进行修改。这就如在一个真实的计算机上,为每一种移动存储设备实现一个不同的插口、并分别有各自的驱动程序。当有了一种新的移动存储设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显然不可取。

此方案的另一个缺点在于,冗余代码多。如果有100种移动存储,那我们的Computer中岂不是要至少写200个方法,这是不能接受的!

我们再来看方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。

我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动设备,并且都实现了共同的接口,所以不管有多少种移动设备,只要一个Read方法和一个Write方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将移动存储类硬编码在Computer中,所以有了新的第三方设备,完全可以替换进去运行。这就是所谓的“依赖接口,而不是依赖与具体类”,不信你看看,Computer类只有一个MobileStorage类型或IMobileStorage类型的成员变量,至于这个变量具体是什么类型,它并不知道,这取决于我们在运行时给这个变量的赋值。如此一来,Computer和移动存储器类的耦合度大大下降。

那么这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里,我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口。

最后我们再来看一看方案四,它和方案三很类似,只是将“可读”和“可写”两个规则分别抽象成了接口,然后让IMobileStorage再继承它们。这样做,显然进一步提高了灵活性,但是,这有没有设计过度的嫌疑呢?我的观点是:这要看具体情况。如果我们的应用中可能会出现一些类,这些类只实现读方法或只实现写方法,如只读光盘,那么这样做也是可以的。如果我们知道以后出现的东西都是能读又能写的,那这两个接口就没有必要了。其实如果将只读设备的Write方法留空或抛出异常,也可以不要这两个接口。总之一句话:理论是死的,人是活的,一切从现实需要来,防止设计不足,也要防止设计过度。

在这里,我们姑且认为以后的移动存储都是能读又能写的,所以我们选方案三。

实现


下面,我们要将解决方案加以实现。我选择的语言是C#,但是在代码中不会用到C#特有的性质,所以使用其他语言的朋友一样可以参考。

首先编写IMobileStorage接口:

Code:IMobileStorage

1namespace InterfaceExample
2{
3    public interface IMobileStorage
4    {
5        void Read();//从自身读数据
6        void Write();//将数据写入自身
7    }

8}

 

代码比较简单,只有两个方法,没什么好说的,接下来是三个移动存储设备的具体实现代码:

U盘

Code:FlashDisk

 1namespace InterfaceExample
 2{
 3    public class FlashDisk : IMobileStorage
 4    {
 5        public void Read()
 6        {
 7            Console.WriteLine("Reading from FlashDisk……");
 8            Console.WriteLine("Read finished!");
 9        }

10
11        public void Write()
12        {
13            Console.WriteLine("Writing to FlashDisk……");
14            Console.WriteLine("Write finished!");
15        }

16    }

17}


MP3

Code:MP3Player

 1namespace InterfaceExample
 2{
 3    public class MP3Player : IMobileStorage
 4    {
 5        public void Read()
 6        {
 7            Console.WriteLine("Reading from MP3Player……");
 8            Console.WriteLine("Read finished!");
 9        }

10
11        public void Write()
12        {
13            Console.WriteLine("Writing to MP3Player……");
14            Console.WriteLine("Write finished!");
15        }

16
17        public void PlayMusic()
18        {
19            Console.WriteLine("Music is playing……");
20        }

21    }

22}


移动硬盘

Code:MobileHardDisk

 1namespace InterfaceExample
 2{
 3    public class MobileHardDisk : IMobileStorage
 4    {
 5        public void Read()
 6        {
 7            Console.WriteLine("Reading from MobileHardDisk……");
 8            Console.WriteLine("Read finished!");
 9        }

10
11        public void Write()
12        {
13            Console.WriteLine("Writing to MobileHardDisk……");
14            Console.WriteLine("Write finished!");
15        }

16    }

17}


可以看到,它们都实现了IMobileStorage接口,并重写了各自不同的Read和Write方法。下面,我们来写Computer:

Code:Computer

 1namespace InterfaceExample
 2{
 3    public class Computer
 4    {
 5        private IMobileStorage _usbDrive;
 6
 7        public IMobileStorage UsbDrive
 8        {
 9            get
10            {
11                return this._usbDrive;
12            }

13            set
14            {
15                this._usbDrive = value;
16            }

17        }

18
19        public Computer()
20        {
21        }

22
23        public Computer(IMobileStorage usbDrive)
24        {
25            this.UsbDrive = usbDrive;
26        }

27    
28        public void ReadData()
29        {
30            this._usbDrive.Read();
31        }

32
33        public void WriteData()
34        {
35            this._usbDrive.Write();
36        }

37    }

38}


其中的UsbDrive就是可替换的移动存储设备,之所以用这个名字,是为了让大家觉得直观,就像我们平常使用电脑上的USB插口插拔设备一样。

OK!下面我们来测试我们的“电脑”和“移动存储设备”是否工作正常。我是用的C#控制台程序,具体代码如下:

Code:测试代码

 1namespace InterfaceExample
 2{
 3    class Program
 4    {
 5        static void Main(string[] args)
 6        {
 7            Computer computer = new Computer();
 8            IMobileStorage mp3Player = new MP3Player();
 9            IMobileStorage flashDisk = new FlashDisk();
10            IMobileStorage mobileHardDisk = new MobileHardDisk();
11
12            Console.WriteLine("I inserted my MP3 Player into my computer and copy some music to it:");
13            computer.UsbDrive = mp3Player;
14            computer.WriteData();
15            Console.WriteLine();
16
17            Console.WriteLine("Well,I also want to copy a great movie to my computer from a mobile hard disk:");
18            computer.UsbDrive = mobileHardDisk;
19            computer.ReadData();
20            Console.WriteLine();
21
22            Console.WriteLine("OK!I have to read some files from my flash disk and copy another file to it:");
23            computer.UsbDrive = flashDisk;
24            computer.ReadData();
25            computer.WriteData();
26            Console.ReadLine();
27        }

28    }

29}


现在编译、运行程序,如果没有问题,将看到如下运行结果:

图2.1 各种移动存储设备测试结果

好的,看来我们的系统工作良好。

后来……


 

刚过了一个星期,就有人送来了新的移动存储设备NewMobileStorage,让我测试能不能用,我微微一笑,心想这不是小菜一碟,让我们看看面向接口编程的威力吧!将测试程序修改成如下:

Code:测试代码

 1namespace InterfaceExample
 2{
 3    class Program
 4    {
 5        static void Main(string[] args)
 6        {
 7            Computer computer = new Computer();
 8            IMobileStorage newMobileStorage = new NewMobileStorage();
 9
10            Console.WriteLine("Now,I am testing the new mobile storage:");
11            computer.UsbDrive = newMobileStorage;
12            computer.ReadData();
13            computer.WriteData();
14            Console.ReadLine();
15        }

16    }

17}


编译、运行、看结果:

哈哈,神奇吧,Computer一点都不用改动,就可以使新的设备正常运行。这就是所谓“对扩展开放,对修改关闭”。

图2.2 新设备扩展测试结果

又过了几天,有人通知我说又有一个叫SuperStorage的移动设备要接到我们的Computer上,我心想来吧,管你是“超级存储”还是“特级存储”,我的“面向接口编程大法”把你们统统搞定。

但是,当设备真的送来,我傻眼了,开发这个新设备的团队没有拿到我们的IMobileStorage接口,自然也没有遵照这个约定。这个设备的读、写方法不叫Read和Write,而是叫rd和wt,这下完了……不符合接口啊,插不上。但是,不要着急,我们回到现实来找找解决的办法。我们一起想想:如果你的Computer上只有USB接口,而有人拿来一个PS/2的鼠标要插上用,你该怎么办?想起来了吧,是不是有一种叫“PS/2-USB”转换器的东西?也叫适配器,可以进行不同接口的转换。对了!程序中也有转换器。

这里,我要引入一个设计模式,叫“Adapter”。它的作用就如现实中的适配器一样,把接口不一致的两个插件接合起来。由于本篇不是讲设计模式的,而且Adapter设计模式很好理解,所以我就不细讲了,先来看我设计的类图吧:

如图所示,虽然SuperStorage没有实现IMobileStorage,但我们定义了一个实现IMobileStorage的SuperStorageAdapter,它聚合了一个SuperStorage,并将rd和wt适配为Read和Write,SuperStorageAdapter

图2.3 Adapter模式应用示意

具体代码如下:

Code:SuperStorageAdapter

 1namespace InterfaceExample
 2{
 3    public class SuperStorageAdapter : IMobileStorage
 4    {
 5        private SuperStorage _superStorage;
 6
 7        public SuperStorage SuperStorage
 8        {
 9            get
10            {
11                return this._superStorage;
12            }

13            set
14            {
15                this._superStorage = value;
16            }

17        }

18    
19        public void Read()
20        {
21            this._superStorage.rd();
22        }

23
24        public void Write()
25        {
26            this._superStorage.wt();
27        }

28    }

29}


好,现在我们来测试适配过的新设备,测试代码如下:

Code:测试代码

 1namespace InterfaceExample
 2{
 3    class Program
 4    {
 5        static void Main(string[] args)
 6        {
 7            Computer computer = new Computer();
 8            SuperStorageAdapter superStorageAdapter = new SuperStorageAdapter();
 9            SuperStorage superStorage = new SuperStorage();
10            superStorageAdapter.SuperStorage = superStorage;
11
12            Console.WriteLine("Now,I am testing the new super storage with adapter:");
13            computer.UsbDrive = superStorageAdapter;
14            computer.ReadData();
15            computer.WriteData();
16            Console.ReadLine();
17        }

18    }

19}


运行后会得到如下结果:

图2.4 利用Adapter模式运行新设备测试结果

OK!虽然遇到了一些困难,不过在设计模式的帮助下,我们还是在没有修改Computer任何代码的情况下实现了新设备的运行。

 好了,理论在第一篇讲得足够多了,所以这里我就不多讲了。希望各位朋友结合第一篇的理论和这个例子,仔细思考面向接口的问题。当然,不要忘了结合现实。

下一篇,我将解析经典设计模式中的面向接口编程思想和.NET平台分层架构中接口的运用。

作者:T2噬菌体
出处:http://leoo2sk.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-04-11 15:49 EricZhang(T2噬菌体) 阅读(6904) 评论(99)  编辑 收藏 网摘 所属分类: 面向对象技术

  回复  引用  查看    
#1楼2008-04-11 16:13 | 雨中漫步的太阳      
不错 讲的很生动
  回复  引用    
#2楼2008-04-11 16:23 | SuperLouis[未注册用户]
谢谢楼主,使我对面向接口编程有了更多的了解。
  回复  引用  查看    
#3楼2008-04-11 16:30 | 白发先生      
不错,期待下文!
  回复  引用    
#4楼2008-04-11 16:37 | HxBin[未注册用户]
讲得不错,结合现实让人很容易看懂,最重要还是有结合代码,加深理解。。
  回复  引用  查看    
#5楼2008-04-11 16:44 | lovecherry      
从接口讲到适配器模式,很清晰
  回复  引用  查看    
#6楼2008-04-11 16:53 | Tony Zhou      
同意楼上所有人!
  回复  引用  查看    
#7楼2008-04-11 16:55 | Anytao      
和我在《你必须知道的.NET》1.4节 “多态的艺术”中实现的万能加载器很相似,呵呵。不过我强调的是多态,LZ讲述接口,异曲同工:-)
实例有异,思想兼容,好文章。

  回复  引用  查看    
#8楼[楼主]2008-04-11 17:02 | T2噬菌体      
@Anytao
您就是《你必须知道的.NET》的作者吧,真没想到能得到微软MVP的肯定,真是受宠若惊。您最近的这本新书还没有买到,不过一定会买来看看的。因为我看了目录,很喜欢这种讲思想的书。

我现在还只是一名在读的本科生,很多思想也比较不完善,毕竟没有在公司里做过项目,欠缺经验,还望多多指点。

  回复  引用  查看    
#9楼2008-04-11 17:16 | JackMa      
关于后半部分Adapter的讲述,由于前面已经使用了接口,所以不应该在SuperStorageAdapter中聚合一个SuperStorage。而应该将SuperStorageAdapter继承SuperStorage,实现IMobileStorage。这样的耦合应该会更低。这里也正好说明面向接口的好处。如果当初我们使用了Abstract Class而不是Interface,那么就只能按楼主的方法了。
发表一下愚见,不对的话请指正。

  回复  引用  查看    
#10楼2008-04-11 17:18 | Anytao      
@T2噬菌体
呵呵,lovecherry才是大牛。看到你的举例,让我产生很多共鸣,对于设计模式、面向对象我也在不断学习,园子中有很多这方面的精品,这篇和上篇多时难得的佳作。
对于接口的理解,找一个合适的示例对于说清问题是很关键的,你的例子相当不错,谢谢啦:-)

  回复  引用  查看    
#11楼2008-04-11 17:22 | good man      
很精点的代码,一定要好好学习,真的编程的思想真是太爽了
支持楼主

  回复  引用  查看    
#12楼[楼主]2008-04-11 17:25 | T2噬菌体      
@JackMa
嗯,如果按您的方法,应该也是可以达到目的的。不过到底哪样耦合程度更低,我觉得有待考究。
第一,用一个适配器去继承被适配对象,我觉得怪怪的,因为在我的心里,类之间的继承(不包括实现接口),一定要确实是一般和特殊的关系,这里适配器肯定不是一个特殊的被适配对象,所以我觉得还是用聚合好。
第二,我觉得一般情况下应该是聚合比继承耦合程度低吧……没太明白您为什么觉得继承的话程度更低……

嗯,我的水平也是一般,希望和你讨论这个问题,指正就不敢了,呵呵

  回复  引用  查看    
#13楼2008-04-11 17:33 | JackMa      
@楼主
确实到底泛化和聚合那个耦合低一点我一时未能查到资料。只是记得那时学Adapter模式时确实有两种方式实现:
一种为Class Adapter,即我所讲的方式。
一种为Object Adapter,即楼主的方式。
当时是推荐使用Class Adatper的。当然,我也不是很记得当时的实例场景。确实,总是应该根据场景来使用的。
继续关注,看看大家的说法。

  回复  引用  查看    
#14楼2008-04-11 17:37 | lovecherry      
在这里对象适配器还是比较合适的,如果需要扩展对被适配类行为的话可以考虑类适配器,一般来说优先考虑组合而不是继承
  回复  引用  查看    
#15楼[楼主]2008-04-11 17:43 | T2噬菌体      
@lovecherry
您就是Anytao说的大牛吧,刚去了你的博客看了看,幸会了,呵呵!多谢您的指点!

也谢谢JackMa,我还真没仔细考虑过类适配器和对象适配器应用场合的问题

  回复  引用  查看    
#16楼2008-04-11 17:51 | lovecherry      
@T2噬菌体
我是初学者,不可想象LZ还是学生,能有这样的思考层次并且愿意分享前途一定不可限量,我在LZ这个年龄的时候还只知道打游戏浪费时间呢。

  回复  引用  查看    
#17楼2008-04-11 17:55 | 无名小卒      
讲的很经典呀
  回复  引用  查看    
#18楼2008-04-11 17:59 | 生鱼片      
浅显易懂,学习
  回复  引用    
#19楼2008-04-11 18:32 | 7733[未注册用户]
添加新设备时,NewMobileStorage也要实现接口吧!
  回复  引用    
#20楼2008-04-11 18:52 | 7733[未注册用户]
楼主: Get/Set UsbDrive属性与
构造函数
23 public Computer(IMobileStorage usbDrive)
24 {
25 this.UsbDrive = usbDrive;
26 }
是否只要其一,似乎没有用到构造函数啊?:)

  回复  引用  查看    
#21楼[楼主]2008-04-11 19:13 | T2噬菌体      
--引用--------------------------------------------------
7733: 添加新设备时,NewMobileStorage也要实现接口吧!
--------------------------------------------------------

恩,那是自然。我后来的两个扩展例子其实是为了说明在实现接口和没有实现接口两种情况下如何使用接口进行扩展。

实现接口时,不必说了,直接挂接就可以了。
而没实现接口,如果实现了对应的功能,则可以使用Adapter模式。

  回复  引用  查看    
#22楼2008-04-11 19:13 | JackMa      
@All
真的汗颜啊,没办法,基础没打好。首先明确一点泛化是类间耦合度最高的关系。晕倒!另外我查回资料,之前看到的例子之所以用Class Adapter,主要是出于扩展的考虑。另外,想想,可能SuperStorage会有一些有用的protected的方法吧。
很高兴在和大家的讨论中更清晰了思路。

  回复  引用  查看    
#23楼[楼主]2008-04-11 19:14 | T2噬菌体      
@7733
嗯,这里确实没有用到这个构造函数,写在这里只是习惯性考虑,在这个例子中,如果去掉也没问题:)

  回复  引用  查看    
#24楼[楼主]2008-04-11 19:20 | T2噬菌体      
@lovecherry

您谦虚了,呵呵

  回复  引用  查看    
#25楼2008-04-11 22:40 | 谦虚的天下      
好文章!
  回复  引用    
#26楼2008-04-12 01:21 | yuanlf[未注册用户]
这样子写是不是mp3的听歌功能没法正常使用了?
  回复  引用  查看    
#27楼2008-04-12 02:25 | 梁逸晨      
好贴好帖好好帖
  回复  引用  查看    
#28楼2008-04-12 06:20 | 金色海洋(jyk)      
我又认真地看了一遍,怎么感觉这些思路应该是程序员的基本功呢?

如果把面向对象的三大特点(包括基类和接口)学透了,并且能够灵活运用的话,这些写法就是水到渠成的事情了。或者说是思路的体现。

讲的确实是挺好的,我现在也能够看懂了。
能看懂的原因是我对面向对象有了一定的了解,如果是以前的我的话,一定是看不懂的。

对了,我认为这里讲了两方面的事情,一个是思路,另一个是用C#的事项方式。

我觉得思路是应该自己摸索或者自己来想到的(当然靠学习也是可以的),用某一种语言来具体实现才是应该学习的。

以上只是我的一点想法,说错的话请多多原谅,先道歉。

  回复  引用  查看    
#29楼[楼主]2008-04-12 07:59 | T2噬菌体      
--引用--------------------------------------------------
yuanlf: 这样子写是不是mp3的听歌功能没法正常使用了?
--------------------------------------------------------
不会啊,只不过在这个例子中没有调用PlayMusic方法罢了。实现接口的类只是要求必须实现接口所规定的全部方法,并不是只能有接口中的方法。它也可以拥有接口没有的方法

  回复  引用  查看    
#30楼[楼主]2008-04-12 08:02 | T2噬菌体      
@金色海洋(jyk)
嗯,说的挺对的。这个例子确实很简单,每一个具备一定面向对象基础的人都能够想到,所以我在本文一开始就说了“为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅”。通过这个例子我只是想说明面向接口编程的一般方法,并不能完整体现出面向接口编程的思想内涵及优势。也许在下一篇,我解析稍大点的项目时,这种思想体现的更彻底:)

  回复  引用  查看    
#31楼2008-04-12 10:21 | 李战      
http://www.cnblogs.com/Emoticons/others/dance2.gif" alt="" />横看接口竖看类,大家看门道,俺就看热闹http://www.cnblogs.com/Emoticons/QQ/laf.gif" alt="" />
  回复  引用    
#32楼2008-04-12 11:50 | yuanlf[未注册用户]
我指的是你实例化的方式:“IMobileStorage mp3Player = new MP3Player();”这个对象(mp3Player)能访问的播放功能吗?
  回复  引用  查看    
#33楼[楼主]2008-04-12 12:04 | T2噬菌体      
@yuanlf
嗯,在这里肯定是不行的,因为我的原意PlayMusic这个方法并不是Computer来调用的,Computer只使用Read和Write方法。
PlayMusic可能会被一个叫Boy或Gril的类调用,那时,将要使用“MP3Player mp3Player = new MP3Player();”这样的语句来实例化

  回复  引用  查看    
#34楼2008-04-12 12:56 | 金色海洋(jyk)      
并不是说你的例子简单,我更算不上什么高手。

因为我最近在想每个人的水平能力的差距的问题。

比如说这个思路使自己想到的,还是看到了别人的文章之后才能想到的。


  回复  引用  查看    
#35楼2008-04-12 17:30 | gguowang      
好文章啊 楼主费心了 多谢啊 !
  回复  引用  查看    
#36楼2008-04-13 03:36 | 悟道2008      
问个问题:
如果我定义了一个接口 IA 里面有一个 M 方法,有类 B 和类 C,但是我发现 B 和 C 类在实现IA接口里的 M 方法的时候需要用到的方法参数不一样,这种情况应该怎么办啊?
期待楼主指点。

  回复  引用  查看    
#37楼[楼主]2008-04-13 09:24 | T2噬菌体      
@悟道2008

这个问题可以这样解决:

1.首先在接口IA中写两个参数不同的M方法

2.B和C实现IA后,其中两个M方法都要重写,对于用不到的另一个M方法,可以让它返回一个表示无意义的值。

另外,我觉得,出现这种情况时,你可以先看看自己的设计,是否有设计不当的地方。

  回复  引用  查看    
#38楼2008-04-13 10:51 | 悟道2008      
@T2噬菌体
我估计是我设计有问题,感觉这样搞,好像特别麻烦。。。恩,谢谢你。。。继续关注中。。。

  回复  引用    
#39楼2008-04-13 21:55 | 糖冒鸡屎[未注册用户]
哈哈 谢谢楼主 解开我已久的苦恼
  回复  引用    
#40楼2008-04-14 03:33 | 锦瑟[未注册用户]
应该是适配器中聚合一个类的方式比较好。因为这次是SuperStorage,下次可能是叫PowerStorage,难道每来一个新设备,你要研发一个新的适配器去继承相应的类?还是用成员的形式放在适配器里,这样比较灵活吧。

关于那个不同参数的问题,能否使用泛型接口,将这个参数写成泛型类呢?

  回复  引用  查看    
#41楼2008-04-14 08:49 | 雨中漫步的太阳      
最近刚刚看设计模式,楼主讲的面向接口编程 前面一段例子似乎使用了设计模式中的"策略模式" 不知道我的理解对不对,欢迎指正
  回复  引用  查看    
#42楼2008-04-14 12:46 | 笑清风      
好文章! lz还是个学生 能理解到这样的层次 真是不简单啊 学习中。。
  回复  引用  查看    
#43楼2008-04-14 13:37 | Inrie(洪晓军)      
喜欢这种叙述方式的文章,读起来很清晰,很舒服。
  回复  引用  查看    
#44楼2008-04-14 14:09 | hyy      
学习了 3ks
  回复  引用    
#45楼2008-04-14 16:30 | 老太[未注册用户]
写得很不错。对于初学者而言,很容易理解。对新人很有参考价值。
  回复  引用  查看    
#46楼2008-04-14 16:58 | andy65007      
讲的太好了,建议你在开个设计模式的系列吧,听你讲真的能听明白。
  回复  引用  查看    
#47楼[楼主]2008-04-14 17:56 | T2噬菌体      
@andy65007
呵呵。其实关于设计模式,我也一直在学习,很多地方还不能弄得很清楚。不过如果大家原意看,我可以试着把我已经理解比较透彻的部分写出来。
但是最近在忙毕业设计和补考的事,可能没太多时间写文章。等今年夏天本科毕业后应该就好多了,研究生应该会相对轻松点吧……

  回复  引用  查看    
#48楼2008-04-14 18:50 | TT.Net      
很生动,代码简洁明了,就是为什么中国人console输出要用洋文呢?
最后还带上了适配器模式。
博主很用功

  回复  引用    
#49楼2008-04-14 20:04 | 小QQ[未注册用户]
非常好,思路清晰.
  回复  引用    
#50楼2008-04-15 09:43 | stevegu1978[未注册用户]
LZ有做好老师的天赋,改造做教育,中国之甚幸
  回复  引用  查看    
#51楼2008-04-15 11:24 | 专研.NET      
写的详细,支持!
  回复  引用    
#52楼2008-04-15 15:12 | 傻子林[未注册用户]
学习了,楼主写的很好。
  回复  引用    
#53楼2008-04-16 10:55 | NingDev[未注册用户]
写的太好了。谢谢楼主。
  回复  引用    
#54楼2008-04-17 15:06 | 夏文俊[未注册用户]
楼主讲得很好````
很容易理解````继续学习下一篇~!

  回复  引用    
#55楼2008-04-22 13:40 | 老太[未注册用户]
写得很好,例子也很生动。我给学生讲接口和抽象类的时候,就用的您的例子。
  回复  引用  查看    
#56楼2008-04-24 13:34 | PerfectDesign      
例子举得很好,非常赞同,而且顺道也提及了适配器模型,很自然地就带过来了,没想到一个学生,技术文笔写得这么好,佩服啊!
  回复  引用    
#57楼2008-04-29 14:38 | 第n个过客[未注册用户]
期待lz出书

  回复  引用    
#58楼2008-05-06 00:05 | ghost5018[未注册用户]
看了两篇楼主的文章,写的挺通俗易懂,没想到楼主居然还是个学生就有如此深刻的理解,向你学习…… 我也是学生,长见识了……
  回复  引用  查看    
#59楼2008-05-06 16:39 | 思考-总结      
@李战
横看接口竖看类。
我一直以为只有我自己才这么想的。之前在CSDN上有人问了一个为什么抽象类还要继承接口的问题,我就说了句 , 接口是方便横向扩展,抽象类是方便纵向扩展,如果横向和纵向都要变化的话,就采用抽象类继承接口的方式。

没想到我的想法和大牛的类似, 荣幸啊~~

  回复  引用  查看    
#60楼2008-05-19 18:23 | LanYo      
看了楼主的文章,感叹
像lovecherry 所说的:像你这个年龄,我还努力学习打游戏呢
我是工作后才真正接触设计模式, 在学校虽然没lovecherry 的疯狂
也学习,但就没达到面象对想的一点点思想!
得特别感谢<大话设计模式>作者程杰和<你必须知道的.NET>作者王涛

  回复  引用  查看    
#61楼2008-05-27 17:45 | 夜风777      
接口+设计模式,更加形象了!
  回复  引用  查看    
#62楼2008-06-07 21:38 | qiangchun      
真的是受益非浅,看了这些,使我对面向接口编程有了更深的了解,谢谢!希望下次能看到你更精彩的文章.期待......

  回复  引用  查看    
#63楼2008-06-17 15:07 | 小庄      
晕啊!楼主的意见我实在不敢苟同啊!
那些USB设备它不能这么简单就被楼主抽象成接口了啊,他们都是“实体”,都应该抽象成抽象类,他们的读和写方法都应该是在操作自己的存储器,跟电脑有个屁关系啊,电脑是通过USB接口对这些设备进行操作的,USB才是楼主要找的接口,这个接口的的主要功能就是在电脑和USB设备之间转发数据(提供服务),可以理解为USB借口实现了电脑这个实体和USB设备实体之间的相互通信,USB设备抽象出来的抽象类只需要能够实现接收(写)和发送(读)USB传过来的数据(参数或返回值),就说明这个设备支持USB了;

要是按照楼主的理解,那我们造那么多种的USB设备干嘛,完全可以定义一个“USB设备接口”,因为接口是暴露给用户(调用者)的,我们每个人拿这个接口,然后再自己在上面进行改造让它变成不同的设备?这有点本末倒置了吧?

  回复  引用  查看    
#64楼2008-06-19 20:35 | jvl      
学习,第一次看贴子连评论都看完的,真的精彩!
  回复  引用  查看    
#65楼2008-06-22 11:00 | 欧尔      

讲得很生动,通俗易懂!
把“接口”的精髓都差不多理出来了!
支持一下!

  回复  引用  查看    
#66楼2008-06-27 17:32 | 艺林      
楼主用的IoC中的设值注入方式
  回复  引用    
#67楼2008-07-06 12:39 | ytu校友[未注册用户]
static void Main(string[] args)
6 {
7 Computer computer = new Computer();
8 IMobileStorage mp3Player = new MP3Player();
9 IMobileStorage flashDisk = new FlashDisk();
10 IMobileStorage mobileHardDisk = new MobileHardDisk();
----------------------------------------------------------------
这两种写法有什么不同:有什么优缺点?
static void Main(string[] args)
{
MP3Player mp3Player = new MP3Player();
FlashDisk flashDisk = new FlashDisk();
MobileHardDisk mobileHardDisk = new MobileHardDisk();

  回复  引用  查看    
#68楼[楼主]2008-07-06 12:51 | T2噬菌体      
@ytu校友

上一种方法实现了多态性,这时把各种移动存储设备都看做 IMobileStorage ,而下一种将他们看做具体的类,不符合面向接口编程的思想

  回复  引用  查看    
#69楼2008-07-09 11:45 | 陈招展      
写得很不错,通过实例,讲得通俗易懂,有当老师的天赋!
  回复  引用    
#70楼2008-07-22 21:37 | 无名[未注册用户]
讲的不错!
  回复  引用  查看    
#71楼2008-07-28 11:50 | 李小彬      
很是学习
  回复  引用  查看    
#72楼2008-07-30 15:54 | 尚希杰      
实在佩服
  回复  引用  查看    
#73楼2008-07-30 15:55 | 尚希杰      
对面向对象的理解忒深刻了。
  回复  引用  查看    
#74楼2008-09-08 14:09 | 水边      
关于泛化和聚合
也是要看情况的,
通常的概念认为,
如果B是A的一种,一般用泛化,即让B继承于A
如果B是A的一部分,一般用聚合,让B与别的东西一起组成A

在楼主举的例子中,因为要开发一个适配器,这个适配器和SuperStorage没有种类关系,而是一种组合关系,所以个人认为不应该用泛化。

  回复  引用  查看    
#75楼2008-09-20 22:39 | dreamskyyu      
SuperStorage super = new SuperStorage();
SuperStorageAdapt superda = new SuperStorageAdapt(super);
Computer computer = new Computer(superda);

computer.WriteData();
computer.ReadData();

Console.ReadKey();

  回复  引用  查看    
#76楼2008-09-20 22:39 | dreamskyyu      
这样写是不是好一点呢
  回复  引用  查看    
#77楼2008-09-25 16:14 | jowo      
讲的不错,学习了
  回复  引用  查看    
#78楼2008-10-22 11:12 | aierong      
good

  回复  引用  查看    
#79楼2008-10-22 11:29 | aierong      
楼主那张关于Adapter设计模式的uml图是怎么画出来的?
用什么工具?

  回复  引用  查看    
#80楼[楼主]2008-10-23 14:42 | T2噬菌体      
@aierong
是VS自带的画类图的工具

  回复  引用  查看    
#81楼2008-11-15 23:38 | 李胜攀      
前段时间着迷于各个设计模式的神奇
后来静下心来,发现其实设计模式还是对面向对象设计的那几个基本原则的实现。我们必须掌握的,不是去记住有哪些模式,而是要去深刻的理解面向对象的思想。
楼主在这方面写的挺好的,接口确实在面向对象设计中占有非常重要的地位。

  回复  引用    
#82楼2008-11-20 11:16 | 无痕羽[未注册用户]
接口的默认访问权限不是public吗?为什么这个例子中不加public会出错?请楼主帮小弟解疑!!!
  回复  引用  查看    
#83楼[楼主]2008-11-20 14:05 | T2噬菌体      
@无痕羽
接口默认属性好像不是public

  回复  引用    
#84楼2008-11-20 16:46 | 无痕羽[未注册用户]
楼主我查了下,是接口里面方法默认是public
  回复  引用  查看    
#85楼[楼主]2008-11-20 21:11 | T2噬菌体      
@无痕羽
没错,接口中的方法默认是public,但是接口默认不是public

  回复  引用    
#86楼2008-11-25 09:55 | aioo[未注册用户]
老大,太感谢了~~
这文章使我学到不少东西!!!

  回复  引用  查看    
#87楼2008-11-25 21:17 | 方庆生      
19 public Computer()
20 {
21 }
22
23 public Computer(IMobileStorage usbDrive)
24 {
25 this.UsbDrive = usbDrive;
26 }

是不是没有必要这样定义了?
7 Computer computer = new Computer();
8 IMobileStorage mp3Player = new MP3Player();
---
computer.UsbDrive = mp3Player;()
此处不是已经定义了吗?

但是你说的东西,我收获不小,我对接口理解了很多。
对接口的引用也有了全新的认识

  回复  引用  查看    
#88楼2008-11-25 21:20 | 方庆生      
你所说的对接口编程的思想,我已经很明白。
非常感谢您的讲解,对于我这样的新手。
正需要您这样的高手的支持,
希望您今后多讲解些。
我会经常关注。

  回复  引用  查看    
#89楼2008-12-04 17:29 | 右手年华      
楼主太强了。
确实太强了。
其实我挺笨的但经楼主这么一说让我明白了不少东西
哈哈,强烈支持

  回复  引用  查看    
#90楼2008-12-04 18:31 | 右手年华      
呵呵,我有一点儿疑问。
我是新手
如果我现在要用接口中没定义的方法
如MP3Player类中的PlayMusic()
是不是要这样啊
((MP3Player)mp3Player).PlayMusic();
确实不太懂,还望楼主指教。

  回复  引用  查看    
#91楼[楼主]2008-12-05 14:08 | T2噬菌体      
@右手年华
恩,如果是通过接口声明的话,得先转换成具体类型

  回复  引用  查看    
#92楼2008-12-09 16:38 | collum      
哪儿有变化,就对哪儿进行抽象。
  回复  引用    
#93楼2008-12-30 22:33 | wdgv[未注册用户]
我认为 最好的设计思想 应该是 63楼的
在你的例子里面所谓的移动硬盘,MP3他们对于MobileStorage的实现只需要一种就是USB接口,都是USB连接
而对它要做的其他扩展应该是IDE接口啊什么的,或者是未来我们还不知道的连接方式。

  回复  引用    
#94楼2009-02-20 10:12 | ooooo[未注册用户]
在差异较大的对像中追求功能上的共性时,使用接口.....
在差异较小的对像中追求功能上的不同时,使用抽像基类

  回复  引用  查看    
#95楼2009-02-23 14:50 | 邱俊      
功力果然深厚!
  回复  引用  查看    
#96楼2009-03-06 11:12 | musicinwater      
谢谢,看懂了。
  回复  引用    
#97楼2009-04-02 13:38 | pie[未注册用户]
这个例子举得非常好啊。
  回复  引用    
#98楼2009-06-18 15:44 | 小才爱学习
学习中 继续发强帖 谢谢




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1148236




相关文章:

相关链接: