[你必须知道的.NET] 第二回:对抽象编程:接口和抽象类

发布日期:2007.4.12 作者:Anytao

©2007 Anytao.com 转贴请注明出处,留此信息。

 本文将介绍以下内容:

• 面向对象思想:多态

• 接口

• 抽象类

 1. 引言

在我之前的一篇post《抽象类和接口的谁是谁非》中,和同事管伟的讨论,得到很多朋友的关注,因为是不成体系的论道,所以给大家了解造成不便,同时关于这个主题的系统性理论,我认为也有必要做以总结,因此才有了本篇的新鲜出炉。同时,我将把上贴中的问题顺便也在此做以交代。

2. 概念引入

  • 什么是接口?

接口是包含一组虚方法的抽象类型,其中每一种方法都有其名称、参数和返回值。接口方法不能包含任何实现,CLR允许接口可以包含事件、属性、索引器、静态方法、静态字段、静态构造函数以及常数。但是注意:C#中不能包含任何静态成员。一个类可以实现多个接口,当一个类继承某个接口时,它不仅要实现该接口定义的所有方法,还要实现该接口从其他接口中继承的所有方法。

定义方法为:


    
public interface System.IComparable
    {
        
int CompareTo(object o);
    }

    
public class TestCls: IComparable
    {
        
public TestCls()
        {
        } 

        
private int _value;
        
public int Value
        {
            
get { return _value; }
            
set { _value = value; }
        } 

        
public int CompareTo(object o)
        {

            
//使用as模式进行转型判断
            TestCls aCls = o as TestCls;
            
if (aCls != null)
            {

            
//实现抽象方法
            return _value.CompareTo(aCls._value);
            }
        }
    }

 

  • 什么是抽象类?

抽象类提供多个派生类共享基类的公共定义,它既可以提供抽象方法,也可以提供非抽象方法。抽象类不能实例化,必须通过继承由派生类实现其抽象方法,因此对抽象类不能使用new关键字,也不能被密封。如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。另外,实现抽象方法由overriding方法来实现。

定义方法为:


/// <summary>
/// 定义抽象类
/// </summary>
abstract public class Animal
{
    
//定义静态字段
    protected int _id; 

    
//定义属性
    public abstract int Id
    {
        
get;
        
set;
    } 

    
//定义方法
    public abstract void Eat(); 

    
//定义索引器
    public string this[int i]
    {
        
get;
        
set;
    } 


/// <summary>
/// 实现抽象类
/// </summary>
public class Dog: Animal
{
    
public override int Id
    {
        
get {return _id;}
        
set {_id = value;}
    } 

    
public override void Eat()
    {
        Console.Write(
"Dog Eats.")
    }


3. 相同点和不同点

3.1 相同点

  • 都不能被直接实例化,都可以通过继承实现其抽象方法。
  • 都是面向抽象编程的技术基础,实现了诸多的设计模式。

3.2 不同点

  • 接口支持多继承;抽象类不能实现多继承。
  • 接口只能定义抽象规则;抽象类既可以定义规则,还可能提供已实现的成员。
  • 接口是一组行为规范;抽象类是一个不完全的类,着重族的概念。
  • 接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。
  • 接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法;抽象类可以定义字段、属性、包含有实现的方法。 
  • 接口可以作用于值类型和引用类型;抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。

 通过相同与不同的比较,我们只能说接口和抽象类,各有所长,但无优略。在实际的编程实践中,我们要视具体情况来酌情量才,但是以下的经验和积累,或许能给大家一些启示,除了我的一些积累之外,很多都来源于经典,我相信经得起考验。所以在规则与场合中,我们学习这些经典,最重要的是学以致用,当然我将以一家之言博大家之笑,看官请继续。

3.3 规则与场合

  1. 请记住,面向对象思想的一个最重要的原则就是:面向接口编程。
  2. 借助接口和抽象类,23个设计模式中的很多思想被巧妙的实现了,我认为其精髓简单说来就是:面向抽象编程。
  3. 抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
  4. 接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系;
  5. 接口多定义对象的行为;抽象类多定义对象的属性; 
  6. 接口定义可以使用public、protected、internal 和private修饰符,但是几乎所有的接口都定义为public,原因就不必多说了。
  7. “接口不变”,是应该考虑的重要因素。所以,在由接口增加扩展时,应该增加新的接口,而不能更改现有接口。
  8. 尽量将接口设计成功能单一的功能块,以.NET Framework为例,IDisposable、IDisposable、IComparable、IEquatable、IEnumerable等都只包含一个公共方法。
  9. 接口名称前面的大写字母“I”是一个约定,正如字段名以下划线开头一样,请坚持这些原则。
  10. 在接口中,所有的方法都默认为public。 
  11. 如果预计会出现版本问题,可以创建“抽象类”。例如,创建了狗(Dog)、鸡(Chicken)和鸭(Duck),那么应该考虑抽象出动物(Animal)来应对以后可能出现风马牛的事情。而向接口中添加新成员则会强制要求修改所有派生类,并重新编译,所以版本式的问题最好以抽象类来实现。
  12. 从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实实现。
  13. 对抽象类不能使用new关键字,也不能被密封,原因是抽象类不能被实例化。
  14. 在抽象方法声明中不能使用 static 或 virtual 修饰符。

以上的规则,我就厚颜无耻的暂定为T14条吧,写的这么累,就当一时的奖赏吧。大家也可以互通有无,我将及时修订。

4. 经典示例

4.1 绝对经典

.NET Framework是学习的最好资源,有意识的研究FCL是每个.NET程序员的必修课,关于接口和抽象类在FCL中的使用,我有以下的建议:

  1. FCL对集合类使用了基于接口的设计,所以请关注System.Collections中关于接口的设计实现;
  2. FCL对数据流相关类使用了基于抽象类的设计,所以请关注System.IO.Stream类的抽象类设计机制。

4.2 别样小菜

下面的实例,因为是我的理解,因此给经典打上“相对”的记号,至于什么时候晋升为“绝对”,就看我在.NET追求的路上,是否能够一如既往的如此执着,因此我将把相对重构到绝对为止(呵呵)。 本示例没有阐述抽象类和接口在设计模式中的应用,因为那将是另一篇有讨论价值的文本,本文着眼与概念和原则的把握,但是真正的应用来自于具体的需求规范。

设计结构如图所示:

 

1. 定义抽象类

    public abstract class Animal
    {
        
protected string _name;

        
//声明抽象属性
        public abstract string Name
        {
            
get;
        }

        
//声明抽象方法
        public abstract void Show();

        
//实现一般方法
        public void MakeVoice()
        {
            Console.WriteLine(
"All animals can make voice!");
        }
    }


2. 定义接口

    public interface IAction
    {
        
//定义公共方法标签
        void Move();
    }


3. 实现抽象类和接口


    
public class Duck : Animal, IAction
    {
        
public Duck(string name)
        {
            _name 
= name;
        }

        
//重载抽象方法
        public override void Show()
        {
            Console.WriteLine(_name 
+ " is showing for you.");
        }

        
//重载抽象属性
        public override string Name
        {
            
get { return _name;}
        }

        
//实现接口方法
        public void Move()
        {
            Console.WriteLine(
"Duck also can swim.");
        }

    }

    
public class Dog : Animal, IAction
    {
        
public Dog(string name)
        {
            _name 
= name;
        }

        
public override void Show()
        {
            Console.WriteLine(_name 
+ " is showing for you.");
        }

        
public override string Name
        {
            
get { return _name; }
        }

        
public void Move()
        {
            Console.WriteLine(_name 
+ " also can run.");
        }

    }


4. 客户端实现


    
public class TestAnmial
    {
        
public static void Main(string [] args)
        {
            Animal duck 
= new Duck("Duck");
            duck.MakeVoice();
            duck.Show();

            Animal dog 
= new Dog("Dog");
            dog.MakeVoice();
            dog.Show();

            IAction dogAction 
= new Dog("A big dog");
            dogAction.Move();
        }
    }


5. 他山之石

正所谓真理是大家看出来的,所以将园子里有创新性的观点潜列于此,一是感谢大家的共享,二是完善一家之言的不足,希望能够将领域形成知识,受用于我,受用于众。

  • dunai认为:抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。至于他们在各个语言中的句法,语言细节并不是我关心的重点。
  • 桦山涧的收藏也很不错。
  • Artech认为:所代码共用和可扩展性考虑,尽量使用Abstract Class。当然接口在其他方面的优势,我认为也不可忽视。
  • shenfx认为:当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。

          最后,MSDN的建议是:

  • 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
  • 如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
  • 如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。
  • 如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。

6. 结论

接口和抽象类,是论坛上、课堂间讨论最多的话题之一,之所以将这个老话题拿出来再议,是因为从我的体会来说,深刻的理解这两个面向对象的基本内容,对于盘活面向对象的抽象化编程思想至关重要。本文基本概况了接口和抽象类的概念、异同和使用规则,从学习的观点来看,我认为这些总结已经足以表达其核心。但是,对于面向对象和软件设计的深入理解,还是建立在不断实践的基础上,Scott说自己每天坚持一个小时用来写Demo,那么我们是不是更应该勤于键盘呢。对于接口和抽象类,请多用而知其然,多想而知其奥吧。

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

©2007 Anytao.com 转贴请注明出处,留此信息。

本贴子以现状提供且没有任何担保,同时也没有授予任何权利。
This posting is provided "AS IS" with no warranties, and confers no rights.

posted @ 2007-04-12 14:33 Anytao 阅读(18174) 评论(191)  编辑 收藏 网摘 所属分类: 01 [你必须知道的.NET]

评论共2页: 上一页 1 2 
  回复  引用  查看    
#101楼2008-01-18 17:07 | 破曉之陽      
可惜,看得不是很明白。也許是沒有認真看吧。上一講就看的很明白。也許上一講非常容易把。嘿嘿。
加油。

  回复  引用  查看    
#102楼[楼主]2008-01-18 20:22 | Anytao      
@破曉之陽
有哪些地方不明白呢?可以邮件讨论:-)
另外,对于接口和抽象类,还应当从设计的角度来参考,才会体会更深,你必须知道的.NET一定会给你更多答案:-)

  回复  引用    
#103楼2008-02-03 16:25 | zhenli627[未注册用户]
写的挺好,看了不能白看,顶一个!
  回复  引用  查看    
#104楼[楼主]2008-02-04 09:34 | Anytao      
@zhenli627
呵呵,谢谢,常来常新:-)

  回复  引用    
#105楼2008-02-22 12:23 | Cloud87[未注册用户]
看是有看,但是还是不能明白全部...为什么一定要通过这么麻烦的方法...来调用一个‘方法’呢?...

public class MyBaseClass : IMyInterface
{
public void DoSomething()
{
Console.WriteLine("Call interface method.");
}

为什么我不能直接通过 MyBaseClass 调用呢?
public static void Main(string[] args)
{
MyClassWithCallback mc = new MyClassWithCallback();
MyBaseClass mbc = new MyBaseClass();

mbc.DoSomething();
}

为什么一定要通过那么麻烦的方法...来调用呢?
using System;

namespace Anytao.net.My_Must_net
{
///
/// 使用接口实现回调示例
///
public interface IMyInterface
{
void DoSomething();
}

public class MyBaseClass : IMyInterface
{
public void DoSomething()
{
Console.WriteLine("Call interface method.");
}

public static void Main(string[] args)
{
MyClassWithCallback mc = new MyClassWithCallback();
IMyInterface mi = new MyBaseClass();

mc.AddCallback(mi);
mc.DoSomething();
mc.RemoveCallback();
}
}

public class MyClassWithCallback
{
private IMyInterface _myInterface;

public void AddCallback(IMyInterface myInterface)
{
_myInterface = myInterface;
}

public void RemoveCallback()
{
_myInterface = null;
}

public void DoSomething()
{
if (_myInterface != null)
_myInterface.DoSomething();
}
}
}

难道调用的结果会有不同?
这就是我不明白的。。。我猜我是不明白某些思想。。
希望楼主能帮帮忙....把答案send去我的email...感激不尽!!!



  回复  引用  查看    
#106楼[楼主]2008-03-03 12:59 | Anytao      
@Cloud87
您的问题很不错,一针见血的点明了为什么“回调”这一话题,其实这是一个需要以专门的篇幅进行说明的问题,远不是一言两语所能表达清楚,所以也引起我极大的兴趣,可能在3月什么时候利用空闲完成一篇关于“回调”机制的文章。在此也谢谢你的讨论。
在此,我仅仅从一个简单的角度来对你的问题进行一点探讨。关于这个问题,落脚点其实关注于回调机制的理解上,而不是具体的方法调用上。这正如下面的示例:
Father father = new Son();
father.DoSonWork();
而不是
Son son = new Son();
son.DoSonWork();
此处体现的是面向对象的抽象机制。正如你所说,这是一种“思想”。而本例中实现“复杂”的方法调用,则是为了说明接口技术也可以支持实现回调机制,也可以认为是一种思想的体现。所以,关于你的问题应该关注于“回调”机制上面。
所以,在此就简单的说说回调是怎么回事?由此来解答你的问题和疑惑。欲了解回调,则必须清楚函数指针,在此不多做介绍,你可以查阅相关资料进行研究。
首先,回调并非.NET中的新鲜玩意,早在C语言中就可以通过指针实现回调函数。回调在程序设计中有着典型的应用,可以说四处充斥着回调的味道和应用,例如.NET中的委托和事件,Windows中的消息机制都是典型的回调应用。
接着我们简单的了解一下“什么是回调?”。熟悉依赖倒置原则的朋友,都会熟记一段经典的好莱坞法则:不要调用我们,我们会调用你。所以,回调实现的是这样一种机制:对象在外部注册一个方法,当事件发生时可以通知外界方法,来执行某个过程。这样实现的好处是,对象方法的调用由被动变为主动。一个形象的例子就是:一个“繁忙的”地主,如果监视长工在地里干活的进度,就会不断的去地头看看,以便了解工作情况,这显然是一种糟糕的工作方式;而明智的做法就是地主命令长工定期去给自己报告他工作的进度,自己只需要优哉的在家里等着长工的报告即可,这就是一种典型的回调机制在实际生活的体现。你看回调的好处就是彻底解放了地主,使得原本繁忙的工作变得简单。
大致的了解了回调机制,我们再回到你的问题,应该就有了更明确的答案。在你提出问题的示例中,通过
mc.AddCallback(mi);
mc.DoSomething();
mc.RemoveCallback();

来调用MyBaseClass类的DoSomething方法,正是一种回调机制的体现。通过AddCallback和RemoveCallBack来进行回调注册和解除,也就相当于长工MyClassWithCallback向地主MyBaseClass打了报告,地主不需要自己去DoSomething了,而全由长工来自己报告。不过,当前的示例因为没有任何涉及实际回调的寓意,而是单纯的来阐述以接口实现回调的技术细节,所以在理解上有些困难。
然而,回调并非尽善尽美,因为一定程度上破坏了程序的结构,所以只能是善用者用之用之。
因为最近忙于新书《你必须知道的.NET》,所以没能及时回复你的提问,还望见谅,希望能解答你的问题,同时祝工作顺利。

  回复  引用    
#107楼2008-03-09 20:17 | Cloud87[未注册用户]
感谢你的回答...我好崇拜你哦!不懂我什么时候才能到达跟你一样的水平...
你不只厉害..而且很乐意回答我们的问题...
如果能的话...我想知道更容易跟你联络的方法(如:msn,yahoo)
当然...我知道能得到你的联络方法的机会很小...但是我还是像试试看
如果不能的话...没关系...我还是非常感谢你^^
我会时常来看你的文章的^^
希望有一天...我能像你一样...写自己的文章和别人分享...(还很遥远~~~~)

  回复  引用  查看    
#108楼[楼主]2008-03-09 20:32 | Anytao      
你好。这么说太客气了。总的说来,我在博客园收获了很多朋友的帮助,这里是个热心的地方,所以我很有兴趣和大家共享技术、互通有无。
我的联系方式为:

MSN:taotalk@hotmail.com
Mail: anytao@live 或者 taoland@126.com

来函请注明博客园


  回复  引用    
#109楼2008-03-10 20:54 | renchengyong[未注册用户]
客户端不应该直接与接口打交道,在接口下应该有抽象类的实现,由抽象类实现接口中的所有方法,客户端继承自抽象类,这样即时接口有改变也不会波及到客户端的使用
  回复  引用  查看    
#110楼[楼主]2008-03-10 21:27 | Anytao      
@renchengyong
谢谢提醒,实际的设计中,保证接口的稳定性是个至关重要的设计原则。其实通过抽象类进一步实现接口,也是面向抽象编成的一个技巧性体现。不管是保留接口还是暴露抽象类,还是以具体的需求而定,没有一定的定论。

  回复  引用  查看    
#111楼2008-03-12 15:39 | hery_2007      
在写代码过程中体会
  回复  引用    
#112楼2008-03-12 16:17 | SAIZONE[未注册用户]
写的挺清楚,看前面有人评论标题

我感觉如果叫 你应该知道的.net

yi应该就不会有盛气凌人的感觉了
没什么必须不必须的,不知道接口 抽象类 一样可以实现一个应用程序
但是好坏显而易见。

总之,内容好的文章不会因标题而失色,加油啊

  回复  引用  查看    
#113楼[楼主]2008-03-12 16:43 | Anytao      
@hery_2007
确实如此,实际的设计还要依赖于客观的需求,这是不变的原则

  回复  引用  查看    
#114楼[楼主]2008-03-12 16:46 | Anytao      
@SAIZONE
呵呵,谢谢你的分析,确实如此。因为从一开始有了一个盛气凌人的名字,中间或多或少有些朋友提出过异议。不过,《你必须知道的.NET》作为一个系列,已经有很长的时间了,实在不忍心更名,就继续吧。不过,我能做的是尽量保证内容的精彩:-)

  回复  引用  查看    
#115楼2008-03-19 09:40 | 狼Robot      
学习.
  回复  引用  查看    
#116楼[楼主]2008-03-19 10:05 | Anytao      
@狼Robot
同学同学:-)

  回复  引用    
#117楼2008-03-19 11:38 | Cloud87[未注册用户]
看了几遍...还是有好多不懂得地方...在这里我就把我不懂得地方发问...希望各位大大能帮帮忙....感激不尽....
  回复  引用    
#118楼2008-03-19 12:09 | Cloud87[未注册用户]
问题1:
public interface System.IComparable
{
int CompareTo(object o);
}

public class TestCls: IComparable
{
public TestCls()
{
}

private int _value;
public int Value
{
get { return _value; }
/**这边的value是Value?编写错误? **/
set { _value = value; }
}

public int CompareTo(object o)
{

//使用as模式进行转型判断
TestCls aCls = o as TestCls;
if (aCls != null)
{

//实现抽象方法
//这里调用的CompareTo是TestCls类的CompareTo?
//这里完全不明白是用来做什么的
//但我们呼叫CompareTo()方法...CompareTo()方法里又呼叫另一个CompareTo()
//这样不就成为一个无限循环吗...
return _value.CompareTo(aCls._value);
}
}
}

  回复  引用    
#119楼2008-03-19 13:17 | Cloud87[未注册用户]
问题2:
实现接口不就是重写方法吗?

public interface IAction
{
//定义公共方法标签
void Move();
}

//就算我们不继承借口...我们也可以是使用Move()方法...不是吗??
public class Dog : Animal //IAction
{
public Dog(string name)
{
_name = name;
}

public override void Show()
{
Console.WriteLine(_name + " is showing for you.");
}

public override string Name
{
get { return _name; }
}

public void Move()
{
Console.WriteLine(_name + " also can run.");
}

}

以我的理解,使用接口是因为我们要把某些程式组合起来...以便以后可以比较容易创建新的程式...
例如:我们创建一个“人”抽象类,然后再派生一个类“学生”类,然后创建一个“IAction”接
口,里边有一个DoSomething()方法.
public abstract class human
{
protected string _name;

public abstract string Name
{
get;
}


}
public interface IAction
{
void DoSomething();
}

public student:human, IAction
{
public student(string name)
{
_name = name;
}
public void DoSomething()
{
Console.WriteLine("study");
}
}

这里也是一样...就算没有借口也是可以的...
我们使用接口和抽象类是因为要把它们组合起来.
(学生)是个(人)他拥有(人)的性质“名字”他的(工作)是“读书”...
所以我们把他们拥有的特性都组合起来...
(身份)是个(人)他拥有(人)的性质“名字”他的(工作)是“XX”...
如果我们要创建新的(身份)时我们只需把他们的性质和工作更改...
同样的我们也可以不使用接口和抽象类来完成...
public student {
private _name
public student(string name)
{
_name = name;
}
public void DoSomething()
{
Console.WriteLine("study");
}
}
但是当创建新的(身份)时,我们又要重新想过(人)有什么特性,他是否会DoSomething()?
所以我们使用接口和抽象类(身份)拥有的东西组合起来。
我的理解有错误吗?请各位大大告诉我...
先问到这里...由于打字太慢和不懂怎样表达...用了X小时才问了两题...明天才继续问...
呵呵。

  回复  引用    
#120楼2008-03-20 19:44 | SAIZONE[未注册用户]
@Cloud87

回答你第二个问题

接口不是重写方法 而是实现方法,接口只是提出了一个方法名,具体的功能你要提供

接口主要用来为各个对象指定需要实现的方法名,这些对象可能没什么联系

你可以把接口想象成手机充电器的端口,这里就是显然没有用接口,每个手机都自己提供一个自己的端口充电,我换个手机原来的充电器就不能用了。

如果手机(对象)都实现用相同的端口(接口),那充电器(充电器对象)不需要做调整就能正常使用,甚至如果我的笔记本(新的对象)也实现了这个端口(同一个接口)这个充电器(充电器对象)也能正常使用

  回复  引用  查看    
#121楼[楼主]2008-03-21 10:00 | Anytao      
@Cloud87
第一个问题:

首先
private int _value;
public int Value
{
get { return _value; }
/value是set访问器的一个隐含参数,为小写,用于从客户端传值
set { _value = value; }
}

例如,如果重新定义一个属性:
private string _name;
public string Name
{
get {return _value;}
set {_name = value;} //value是一个隐含参数
}


public int CompareTo(object o)
{

//使用as模式进行转型判断
TestCls aCls = o as TestCls;
if (aCls != null)
{
//_value本身是int型,所以_value.CompareTo调用的是Int.CompareTo
//而不是TestInterface.CompareTo,不存在任何循环调用
return _value.CompareTo(aCls._value);
}
}
}

  回复  引用  查看    
#122楼[楼主]2008-03-21 10:08 | Anytao      
@Cloud87
SAIZONE的举例很且贴。正如你所说,Student可以不需要继承抽象类,可以不需要实行接口,一样可以实现具有Name属性和DoSomething行为,这是无可争议的。
而接口和抽象类存在的更广泛意义,在于面向抽象编程,面向对象思维。如果在你的当前需求下,如果需要新增一个教师类,那么这个类同样也会具有Name和DoSomething,此时你必须重新思考原来对Student的设计,而面向对象的继承、多态就正体现在了Abstract Class和Interface的灵活设计上了。
这是个需要不断积累的过程,最好了解一下相关的面向对象、设计原则和设计模式,你的思路就会更加开阔。

  回复  引用    
#123楼2008-03-23 22:22 | Cloud87[未注册用户]
感谢你们的回答....明白了很多^^我会常来看看的^^
  回复  引用  查看    
#124楼[楼主]2008-03-23 22:35 | Anytao      
@Cloud87
:-)
欢迎欢迎

  回复  引用  查看    
#125楼2008-04-11 13:43 | 风吹屁屁凉      
你的书国外发行不

想买 买不到

  回复  引用  查看    
#126楼[楼主]2008-04-11 14:03 | Anytao      
@风吹屁屁凉
呵呵,你好,国外应该是没有发行。刚刚出版,还是个新生作品,可以托国内朋友看看,不过邮费实在不划算呐:-)

  回复  引用    
#127楼2008-04-12 10:41 | ljh910[未注册用户]
写得不错,支持一下。
  回复  引用  查看    
#128楼[楼主]2008-04-12 22:33 | Anytao      
@ljh910
哈哈,同支持同支持:-)

  回复  引用    
#129楼2008-05-07 20:50 | wizardlun[未注册用户]
今天我又看了一遍,謝謝樓主,學習中
  回复  引用  查看    
#130楼[楼主]2008-05-07 22:29 | Anytao      
@wizardlun
呵呵,谢谢关注,一如既往:-)

  回复  引用  查看    
#131楼2008-05-27 15:12 | agp001      
内容和纸介质的内容一样嘛
  回复  引用  查看    
#132楼[楼主]2008-05-27 17:52 | Anytao      
@agp001
事实上,还是有很多的不同,书上的内容更加完善和完整,也仔细修改了部分问题。另外,网上已发表内容只有全书内容的1/6内容量:-)

  回复  引用    
#133楼2008-06-23 16:57 | finer[未注册用户]
真的很佩服。

学习中问个问题:

-------------------------
你可以把接口想象成手机充电器的端口,这里就是显然没有用接口,每个手机都自己提供一个自己的端口充电,我换个手机原来的充电器就不能用了。

如果手机(对象)都实现用相同的端口(接口),那充电器(充电器对象)不需要做调整就能正常使用,甚至如果我的笔记本(新的对象)也实现了这个端口(同一个接口)这个充电器(充电器对象)也能正常使用
------------------------

如果有个返回电压的方法,N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V,那他们同样实现了相同的方法,但是也不能通用吧?


谢谢!

  回复  引用    
#134楼2008-06-24 08:00 | Modern[未注册用户]
这是个需要不断积累的过程,最好了解一下相关的面向对象、设计原则和设计模式,你的思路就会更加开阔。
  回复  引用  查看    
#135楼[楼主]2008-06-24 09:50 | Anytao      
@Modern
?
有问题吗?

  回复  引用  查看    
#136楼[楼主]2008-06-24 22:26 | Anytao      
@finer
您好,我不是很明白你所指的:

-------------------------
N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V
-------------------------

这句话的意思,可否以代码实现,这样表述会更清楚,我们再针对代码进行讨论?

谢谢你的讨论:-)

  回复  引用    
#137楼2008-06-25 08:44 | finer[未注册用户]
如果有个返回电压的方法,N记手机实现接口的电压方法时返回的电压是5V,M记实现的方法返回的是3V,那他们同样实现了相同的方法,但是也不能通用吧?
---------------------------------------------------------------

例:
public interface IMobileTelephone
{
int GetVoltage();
}

//N记手机
public class NokiaMobileTelephone:IMobileTelephone
{
//返回5V
public int GetVoltage()
{
//code
}
}

//M记手机
public class MotorolaMobileTelephone:IMobileTelephone
{
//返回3V
public int GetVoltage()
{
//code
}
}

一直不是很懂,请指导一下,谢谢。

  回复  引用  查看    
#138楼[楼主]2008-06-25 22:25 | Anytao      
@finer
我大概理解了你的意思。

其实,在此强调的通用是基于对软件设计中抽象的一种直白的表达。正像你写的示例一样,假设商场中有一个万能充电器MobileCharger,那么它的充电操作可能是这样实现的:

public class MobileCharger
{
public void ChangeForPhone(IMobileTelephone phone)
{
phone.GetVoltage();
//Do else.
}
}

正是有了IMobileTelephone接口的抽象,MobileCharger的ChangeForPhone才能有效的“接受”所有实现了IMobileTelephone接口的手机NokiaMobileTelephone、MotorolaMobileTelephone等等。

类比现实中的实例,你不是也在商场中为不同的手机充电吗,只是插头不同而已,但是对充电器类说,这种“通用”已经足有意义了,反馈回程序设计,正是这种抽象才使得MobileCharger有了通用的魔力:-)

  回复  引用    
#139楼2008-06-26 09:46 | 游戏人间[未注册用户]
我晕,太菜了,都没看懂,,,,怎么办
  回复  引用  查看    
#140楼[楼主]2008-07-09 13:00 | Anytao      
@游戏人间
哪儿不懂,可以明示,共同分析:-)

  回复  引用  查看    
#141楼2008-07-09 13:55 | 剑了      
我得买你的书了!!
  回复  引用  查看    
#142楼[楼主]2008-07-10 22:35 | Anytao      
@剑了
呵呵,谢谢捧场:-)

  回复  引用    
#143楼2008-07-17 22:00 | 静痕[未注册用户]
你的书,很不错```前五章看了三遍,受益无穷啊`````谢谢啦~~~
  回复  引用  查看    
#144楼[楼主]2008-07-18 12:35 | Anytao      
@静痕
呵呵,谢谢支持,有问题可以在此沟通:-)

  回复  引用  查看    
#145楼2008-07-25 21:03 | e旋风      
--引用--------------------------------------------------
xiaowy: 老实说的确总结得不错,但是一看这标题,心中极为不爽!
<br>什么叫 “你必须知道的.NET“ ,为什么我要必须知道?知道了就一定对我有好处吗?
<br>我也是一名搞技术的
<br>
--------------------------------------------------------


你整一欠抽!!
最看不惯你这种跳梁小丑

  回复  引用  查看    
#146楼[楼主]2008-07-25 21:15 | Anytao      
@e旋风
:-)

  回复  引用    
#147楼2008-07-28 16:30 | 9[未注册用户]
我是小菜鸟 我怕WHO
  回复  引用    
#148楼2008-07-28 16:35 | 9[未注册用户]
王哥 敢问您今天多大了 我23了 我不知道要奋斗多少年才能达到您的高度
我也很迷惑 真的

  回复  引用  查看    
#149楼[楼主]2008-07-29 09:02 | Anytao      
@9
呵呵,其实年龄不是制约技术发展的瓶颈,兴趣和坚持才是关键。我也是刚刚赶上80后,比你年长,仍然是个学习者,每天不断学习和充实,并没有强迫和要求,仅仅是兴趣、热爱和事业。

推荐一篇我的讨论,希望能给你以帮助:
http://www.cnblogs.com/anytao/archive/2008/05/28/must_net_20.html" target="_new">http://www.cnblogs.com/anytao/archive/2008/05/28/must_net_20.html

祝好。

  回复  引用    
#150楼2008-07-29 22:00 | toplee[未注册用户]
很仔细的看了你的文章,受益匪浅,谢谢。
顺便问个问题: 《C#高级编程》(第四版) 这本书感觉怎么样? 有必要从头到尾仔仔细细的研读一遍吗? 期待各位的指点:)

  回复  引用  查看    
#151楼[楼主]2008-07-31 01:22 | Anytao      
@toplee
谢谢你的阅读。
关于《C#高级编程》堪称.NET的红宝书,至少值得推荐。关于从头到尾仔细研读,可能不是我的习惯,我认为对于技术专注大部分的书籍不值得通读,而关注专读,取己所需即可,尤其是这种重量级选手我从来没法全读,拿着都重:-)

  回复  引用    
#152楼2008-07-31 13:59 | gismin[未注册用户]
楼主一路走来是看什么书的啦
能推荐下吗 :D

  回复  引用  查看    
#153楼2008-08-30 14:01 | jay-c      
我有一个问题,MSDN当中对接口的定义好像没有涉及到“虚”和“抽象”这两个概念,虚方法必须是以virtual来修饰,而抽象一定要以“abstract”来修饰,而且有很多人说重写接口,接口没有重写这种情况吧,最多就是显示接口调用,那是重写吗??
  回复  引用  查看    
#154楼2008-08-30 14:07 | jay-c      
还有,我认为接口只是一些声明,不应该叫它们虚拟成员,而且重写必须要override,这个东西真的是。。。。
  回复  引用  查看    
#155楼2008-09-01 14:50 | jay-c      
接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;而关键中的关键就在一定要用接口名称进行实例化。
  回复  引用  查看    
#156楼[楼主]2008-09-01 22:58 | Anytao      
  回复  引用  查看    
#157楼[楼主]2008-09-01 23:03 | Anytao      
@jay-c

--引用--------------------------------------------------
jay-c: 我有一个问题,MSDN当中对接口的定义好像没有涉及到“虚”和“抽象”这两个概念,虚方法必须是以virtual来修饰,而抽象一定要以“abstract”来修饰,而且有很多人说重写接口,接口没有重写这种情况吧,最多就是显示接口调用,那是重写吗??
--------------------------------------------------------
很高兴收到你这么多的回复,因为最近外出,没能及时回复,还望见凉。

关于“虚”和“抽象”的概念,你认为什么是“虚”?什么是“抽象”,说实话我从来没有觉得MSDN就一定是正确的,或者本质的。对于接口而言,不过是否显式修饰了virtual,它本质上都是“虚”的,所以任何对接口的实现在本质上都是覆写,而不管是显式的还是隐式的。

  回复  引用  查看    
#158楼[楼主]2008-09-01 23:07 | Anytao      
@jay-c

--引用--------------------------------------------------
jay-c: 还有,我认为接口只是一些声明,不应该叫它们虚拟成员,而且重写必须要override,这个东西真的是。。。。
--------------------------------------------------------

如果非要指教概念上的准确性,我想override被称为“覆写”更为合适。至于“虚拟成员”的说法,我倒认为接口成员更合乎方法签名的说法。

  回复  引用  查看    
#159楼[楼主]2008-09-01 23:13 | Anytao      
@jay-c

--引用--------------------------------------------------

jay-c: 接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;而关键中的关键就在一定要用接口名称进行实例化。

--------------------------------------------------------



关于接口意义的精髓在于就在一句话:接口名称 对象名 = new 实现类名;

这句话,我实在不敢苟同,因为那实在不够揭示接口本质的意义。

接口本质的意义在于约定、契约和规则,接口解决了面向对象中继承所带来的纵向问题,接口实现了更高层次的抽象,这是接口的意义,而“接口名称 对象名 = new 实现类名”只能算是一种应用的表现而非精髓,你举例的应用体现的恰恰是多态的精神,而非接口的精神。



我倒赞赏懒虫对接口的理解。



仅仅是一点己见,欢迎随时讨论,这是非常棒的体验。

  回复  引用    
#160楼2008-09-11 14:52 | X_JKBofer[未注册用户]
  一晚深夜,一个作家闲来无事,忽然兴起,要写本小说。一想好小说的主线路,于是就列好提纲,列好后累了,然后就睡觉去了。所以,就留下了接口,等想起哪天要写时就继续去写。
  一晚深夜,一个作家闲来无事,忽然兴起,要写本小说。一想好小说的主线路,于是就列好提纲,列好后精神很好,就继续写了小说上的内容,东写西写,然后就睡觉去了。所以,就留下了抽象类,等想起哪天要写时就继续去写。  

  回复  引用  查看    
#161楼[楼主]2008-09-18 20:45 | Anytao      
@X_JKBofer


虽然只是关于这二者的一个方面的特性描述,但是
这是我听过的最动人的关于抽象类和接口的故事,谢谢分享:-)

  回复  引用  查看    
#162楼2008-09-22 00:17 | Aadon      
谢谢。。不错不错
  回复  引用  查看    
#163楼[楼主]2008-09-26 10:25 | Anytao      
@Aadon
呵呵,继续努力...

  回复  引用    
#164楼2008-11-26 16:05 | eric_007[未注册用户]
接口实现回调:
回调多个对象或多种类型的对象 要怎么实现...

  回复  引用  查看    
#165楼[楼主]2008-12-20 17:39 | Anytao      
@eric_007
可否仔细说明你的想法?

  回复  引用  查看    
#166楼2009-01-12 17:18 | 赵俊      
大致看完了,总结下对不对:
1、接口比较适合定义关系比较清晰的方法,而且一旦定义就不能再进行修改并且需要完全实现里面定义的方法。
2、抽象类比较设计关系比较复杂的设计(比如存在大量的方法属性或者有共用的方法体实现,并且可能有功能升级方面的需求的)。
不知道理解的对不对,我以前都是用的接口。

  回复  引用    
#167楼2009-02-18 16:15 | feisky
学习受用了,我觉得最大的区别是接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系
  回复  引用  查看    
#168楼[楼主]2009-02-18 16:18 | Anytao      
@赵俊
呵呵,大部分的情况下,接口是非常受用的。

二者的基本比较在本文已经有很多的陈述,而综合来看我觉得还是回到设计思路上更加的能体会接口和抽象类的区别。

呵呵,你的总结很好。

  回复  引用  查看    
#169楼[楼主]2009-02-18 16:18 | Anytao      
@feisky
确实如此。

  回复  引用  查看    
#170楼2009-02-22 09:28 | 银河使者      
接口最常用的就是定义规范,很多规范都使用了接口来定义,如Java中的jsp、servlet、ejb等,C#里也有很多。当然,也可以在自己的项目中用接口定义规范,以使项目一致。
但接口有一个缺点,就是不能代码重用。假设有100个类实现同一个接口,而这个接口中的一个方法在这100个类中至少有50个类的实现是完全一样的,那么使用接口就意味会多编写49个同样的方法,代码大量重复。当然,还有另外的方法,就是使用组合的方式(IOC模式更好),但这也要多编写一些代码。而且设计更复杂。最好的方法就是在接口中加入该方法的默认实现,但这和接口的定义相冲突,接口中不能有具体的实现代码。但在接口中加入实现代码,在实现该接口的子类中继承(也可覆盖)的确是一个非常好的方法。于是面向对象语言的设计者不得不加入了一个新的面向对象元素。这个新元素的目的有如下两个:
1. 也同样可起到定义规范的作用
2. 可以有默认的方法实现
第1点与接口相同,接口就意味着抽象(用来定义抽象规则)。而第2点是类的行为。因此,这些专家们就给这个新的面向对象元素起名为抽象类,含义就是同时兼有接口和类的特性(除了实例化外)。在很多规范中也使用了抽象类来定义规则,因此这些规范需要提供一些默认的实现。我们也可以将接口、抽象类、类看成是一软件中的滑动控件,当滑杆拉到最左边,也就是抽象类中没有任何的默认实现,就是接口,而将滑杆拉到最右边,抽象类中所有的方法都有默认的实现,就类似于类(这里并不等于类,因为抽象类无法实例化,但除了实例化外,其他和普通类已经完全一样了)。如果滑杆在中间的任何位置,就是普通意义的抽象类(至少有一个抽象方法需要子类实现,除非子类也是抽象类)。

另外楼主说"接口可以用于支持回调;抽象类不能实现回调,因为继承不支持。 ",不知楼主说的回调是不是将接口作为参数类型,而在方法中对接口的实现方法进行调用,与IOC模式类似。那么抽象类为什么不行呢,抽象类可是接口和类的联合体,除了实例化,可以完全取代类和接口。麻烦楼主解释一下。还有什么叫”因为继承不支持“。

  回复  引用  查看    
#171楼[楼主]2009-02-23 23:07 | Anytao      
@银河使者
哈哈,真是个有趣的说法:将接口、抽象类、类看成是一软件中的滑动控件。确实是件有意思的事情,不过除了功能上体现为一种滑动偏向外,在面向对象思想上接口和抽象类的定位还确实不能持简单的一统说法,我想这方面的论调已经很多了,也不需要太多语言上的描述了。

呵呵,另外关于”因为继承不支持”,可以详细参考本文第86、87和88楼的讨论,相信已经给出了答案:-)

谢谢你的精彩讨论,值得思考:-)

  回复  引用  查看    
#172楼2009-02-25 19:19 | 银河使者      
@Anytao
看了楼主的解释,原来是为了给类节省这唯一的一次继承的机会。 但这只说明使用接口实现回调或多态更好,而并不是说抽象类不支持,从技术上讲,抽象类完全支持回调或多态,只是不建议使用,就象goto语句,虽然可以使用,但在程序中并不建议过多地使用,但某些特殊情况,还是可以用一下地。
楼主说的“抽象类不能实现回调,因为继承不支持。”感觉就彻底否定了抽象类和回调的关系。有些太绝对了,如果说“抽象类虽然也支持回调,但由于类只支持单继承,并不建议使用”可能更好些。

我们也可以将抽象类看作是接口和类所生的child,抽象类继承了接口和类的部分特性,如抽象方法必须实现(接口),不能实例化(接口),单继承(类),可以有默认的方法实现(类),但又不完全是接口和类。
个人认为抽象类的存在就是为了弥补接口和类的不足,接口虽然可以定义规范,但不能重用,而类虽然可以重用,但又就能定义规范,因此,需要有一个既能定义规范,又能重用的面象对象元素,这就诞生了抽象类。

  回复  引用  查看    
#173楼2009-03-12 23:07 | 会长      
当在差异较大的对象间寻求功能上的共性时,使用接口;当在共性较多的对象间寻求功能上的差异时,使用抽象基类。

抽象类是提取具体类的公因式,而接口是为了将一些不相关的类“杂凑”成一个共同的群体。至于他们在各个语言中的句法,语言细节并不是我关心的重点。

太精辟了!

  回复  引用  查看    
#174楼[楼主]2009-03-18 10:35 | Anytao      
@银河使者
--------------------------------------------
楼主说的“抽象类不能实现回调,因为继承不支持。”感觉就彻底否定了抽象类和回调的关系。有些太绝对了,如果说“抽象类虽然也支持回调,但由于类只支持单继承,并不建议使用”可能更好些。
--------------------------------------------

好像这个说法更确切,我再推敲推敲。

关于抽象类的存在就是为了弥补接口和类的不足,我倒不这么认为,相反我觉得本来就应该存在三种状态:
1 只有定义,没有实现;
2 没有定义,只有实现;
3 既有定义,又有实现。

这样才是事情的全部可能,不是吗?

  回复  引用  查看    
#175楼[楼主]2009-03-18 10:35 | Anytao      
@会长
哈哈,谢谢夸奖:-)

  回复  引用    
#176楼2009-04-01 00:29 | 编程菜鸟
非常经典,楼主幸苦了!
  回复  引用    
#177楼2009-04-01 17:19 | klint
3.3 规则与场合
应该是抽象类可以使用public、protected、internal 和private修饰符

笔误吧

  回复  引用  查看    
#178楼2009-04-13 16:47 | 龙潜冰风悄林      
@Artech认为:所代码共用和可扩展性考虑,尽量使用Abstract Class。当然接口在其他方面的优势,我认为也不可忽视。


我认为这点有出路,设计模式原则:优先使用对象组全,而不是类继承

  回复  引用  查看    
#179楼[楼主]2009-04-13 23:27 | Anytao      
@编程菜鸟
也谢谢你的阅读

  回复  引用  查看    
#180楼[楼主]2009-04-13 23:34 | Anytao      
@klint

感谢你的讨论。
本文所言的接口,一般来说是一CRL角度来理解的,关于接口的访问权限问题,此条同样也适用于C#和VB.NET,例如C#中同样可以以public、private、protected、internal来修饰,当然还包括一个new,主要是用在嵌套接口中。详见:http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/csref/html/vcrefTheInterfaceType.asp" target="_new">http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/csref/html/vcrefTheInterfaceType.asp

其实在28楼已经回答过了

  回复  引用  查看    
#181楼[楼主]2009-04-13 23:36 | Anytao      
@龙潜冰风悄林
多组合,少继承是个基本原则,但要视具体情况而言,也不可一概而论。

组合对变化的隔离是弱依赖的,但是继承的复用机制为子类别提供了更加省力的方式,还是回到具体讨论更好。

  回复  引用  查看    
#182楼2009-04-20 09:35 | 风吹柳絮非      
--引用--------------------------------------------------
feisky: 学习受用了,我觉得最大的区别是接口着重于CAN-DO关系类型,而抽象类则偏重于IS-A式的关系
--------------------------------------------------------
非常同意。

  回复  引用  查看    
#183楼2009-05-12 17:26 | 小猴子      
抽象类和接口结合起来一起用,会带来设计上的本质变化。那感觉真的很爽!
LZ这篇文章写的不错,赞一个啦!

  回复  引用  查看    
#184楼2009-06-18 15:46 | WalkBy      
@Anytao
原来这里“回调机制”跟 C++ 的消息机制 是同根同源的。一语惊醒梦中人,学习了!

评论共2页: 上一页 1 2 



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

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

0 710587




相关文章:

相关链接: