前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的。那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗。博主呵呵一笑,回想当初的自己,不也有此种疑惑么。。。今天打算针对他的问题,结合一个实际的使用场景来说明下抽象类和接口的异同,到底哪些情况需要用接口?又有哪些情况需要用抽象类呢?

C#基础系列目录:

一、业务场景介绍。

博主打算使用原来在华为做外包的时候一个场景:我们针对华为里面的设备做了一个采集设备使用率的程序,设备的类型很多,各种设备的登录和注销方式基本相同,但是每种设备的采集的规则又不太相同。大致的场景就这样,我们来看代码吧。

二、代码示例

根据业务场景,我们简单搭建代码,先来看看代码结构图:

ESTM.Spider:项目的入口程序,只为测试,这里就简单用了一个控制台程序。

ESTM.Spider.Huawei:华为设备采集规则,定义接口抽象实现和具体实现。

ESTM.Utility:解决方案的工具类和接口。

下面来看看具体的实现代码:

1、工具类

  1. namespace ESTM.Utility
  2. {
  3. public class LoginUser
  4. {
  5. public string Username { set; get; }
  6.  
  7. public string Password { set; get; }
  8. }
  9.  
  10. public class Device
  11. {
  12. public string DeviceType { set; get; }
  13.  
  14. public int WaitSecond { set; get; }
  15. }
  16. }

2、接口设计:ISpider.cs

  1. namespace ESTM.Utility
  2. {
  3. //采集接口,定义采集的规则
  4. public interface ISpider
  5. {
  6. bool Login(LoginUser oLoginUser);
  7.  
  8. string Spider(Device oDevice);
  9.  
  10. void LoginOut();
  11. }
  12. }

3、接口抽象实现类:SpiderBase.cs

  1.    /// <summary>
  2. /// 公共的采集基类
  3. /// </summary>
  4. public abstract class SpiderBase : ISpider
  5. {
  6. //华为设备统一采用Telnet方式登录。统一用户名密码都是admin。
  7. public virtual bool Login(LoginUser oLoginUser)
  8. {
  9. Console.WriteLine("华为设备采用Telnet方式登录。");
  10.  
  11. var bRes = false;
  12. if (oLoginUser.Username == "admin" && oLoginUser.Password == "admin")
  13. {
  14. Console.WriteLine("用户名密码校验正确,登录成功");
  15. bRes = true;
  16. }
  17. else
  18. {
  19. Console.WriteLine("用户名密码校验错误,登录失败");
  20. }
  21. return bRes;
  22.  
  23. }
  24.  
  25. //采集操作和具体的设备类型相关,这里用抽象方法,要求子类必须重写
  26. public abstract string Spider(Device oDevice);
  27.  
  28. //华为设备统一注销
  29. public virtual void LoginOut()
  30. {
  31. Console.WriteLine("华为设备采用Telnet方式注销");
  32. }
  33. }

4、接口具体实现类

  1.   [Export("MML", typeof(ISpider))]
  2. public class SpiderMML:SpiderBase
  3. {
  4. //MML设备采集
  5. public override string Spider(Device oDevice)
  6. {
  7. Console.WriteLine("MML设备开始采集");
  8. return "MML";
  9. }
  10. }
  1. [Export("TL2", typeof(ISpider))]
  2. public class SpiderTL2:SpiderBase
  3. {
  4. //TL2设备采集
  5. public override string Spider(Device oDevice)
  6. {
  7. Console.WriteLine("TL2设备开始采集");
  8. return "TL2";
  9. }
  10. }

5、在控制台调用

  1.   class Program
  2. {
  3. [Import("MML", typeof(ISpider))]
  4. public ISpider spider { set; get; }
  5.  
  6. static void Main(string[] args)
  7. {
  8. var oProgram = new Program();
  9. RegisterMEF(oProgram);
  10.  
  11. oProgram.spider.Login(new LoginUser() { Username = "admin", Password = "admin" });
  12. oProgram.spider.Spider(new Device() { DeviceType = "HuaweiDevice", WaitSecond = });
  13. oProgram.spider.LoginOut();
  14. }
  15.  
  16. #region 注册MEF
  17. private static void RegisterMEF(object obj)
  18. {
  19. AggregateCatalog aggregateCatalog = new AggregateCatalog();
  20. var thisAssembly = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
  21. aggregateCatalog.Catalogs.Add(thisAssembly);
  22. var _container = new CompositionContainer(aggregateCatalog, true);
  23. _container.ComposeParts(obj);
  24. }
  25. #endregion
  26. }

6、说明

这是一种比较典型的应用场景。接口定义规则,抽象类定义公共实现或者抽象方法,具体子类实现或者重写抽象类方法。我们重点来看这里的中间桥梁——抽象类。我们知道,抽象类里面既可以有实现的方法,也可以有未实现的抽象方法。

(1)在这里,Login、LoginOut方法由于子类是通用的具有相同逻辑的方法,所以我们需要在抽象类里面去实现这两个方法,如果子类没有特殊需求,调用的时候直接用父类的方法就好了; 如果子类有特殊需求,可以override父类的方法。这样设计既提高了代码的复用率,也可以灵活复写。

(2)另一方面,抽象类里面也定义了抽象方法,这个抽象方法在这里的作用就很好体现了:如果子类不重写父类的抽象方法,编译通不过,直接报错。这样就要求我们子类必须要重写抽象方法。从这点来说,抽象方法和接口的方法申明区别不大。

(3)如果这里不用抽象类,就用一个普通的类来代替行不行?博主的答案是:行!但不好!如果你非要说,我用一个普通的类,将public abstract string Spider(Device oDevice);这个方法写成

  1. public virtual string Spider(Device oDevice)
  2. {
  3. return "";
  4. }

貌似也没问题,反正子类要重写的。确实,这样设计没问题,但是如果你不慎子类忘了override呢?程序还是会跑起来,运行的时候可能会报错。微软既然给我们提供了abstract这么一个东西,我们为什么不用呢。

三、代码扩展

以上我们抽象类使用的必要性和使用方法是介绍完了。那么接下来新的问题来了,可能就有人问了,你上面说了叭叭叭说了这么多,无非就是说了抽象类的必要性,那么既然抽象类这么有用,我们直接用抽象类就好了,你干嘛还要弄一个接口呢。谈到这里,就要说到面向接口编程。其实,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分。或者说,它是面向对象编程体系中的思想精髓之一。而之前博主的文章就分享过面向接口编程的意义所在:依赖倒置,松耦合。那么这里是否可以不要接口,直接用抽象类代替呢?答案还是行!但不好!

比如我们现在又来了新的需求,中兴也要用我们的采集系统,但是它的设备类型、登录注销方式和华为设备区别非常大。那么这个时候我们接口的意义就体现了,如果我们使用接口,我们只需要再重写一个ESTM.Spider.Huawei这个项目就好了,我们暂且命名叫ESTM.Spider.Zhongxing。我们来看看:

代码如下:

  1. namespace ESTM.Spider.Zhongxing
  2. {
  3. /// <summary>
  4. /// 中兴设备采集基类
  5. /// </summary>
  6. public abstract class SpiderBase:ISpider
  7. {
  8. //中兴设备通用登录方法
  9. public virtual bool Login(LoginUser oLoginUser)
  10. {
  11. Console.WriteLine("中兴设备登录前多了一个数据校验:.......");
  12. Console.WriteLine("中兴设备采用WMI方式登录。");
  13.  
  14. var bRes = false;
  15. if (oLoginUser.Username == "root" && oLoginUser.Password == "root")
  16. {
  17. Console.WriteLine("用户名密码校验正确,登录成功");
  18. bRes = true;
  19. }
  20. else
  21. {
  22. Console.WriteLine("用户名密码校验错误,登录失败");
  23. }
  24. return bRes;
  25. }
  26.  
  27. //定义抽象方法,要求子类必须重写
  28. public abstract string Spider(Device oDevice);
  29.  
  30. //中兴设备通用注销
  31. public virtual void LoginOut()
  32. {
  33. Console.WriteLine("中兴设备采用WMI方式注销");
  34. }
  35. }
  36. }
  1. namespace ESTM.Spider.Zhongxing
  2. {
  3. [Export("ZXGC", typeof(ISpider))]
  4. public class SpiderZXGC:SpiderBase
  5. {
  6. public override string Spider(Utility.Device oDevice)
  7. {
  8. Console.WriteLine("中兴ZXGC设备开始采集");
  9. return "ZXGC";
  10. }
  11. }
  12. }
  1. namespace ESTM.Spider.Zhongxing
  2. {
  3. [Export("ZXGY", typeof(ISpider))]
  4. public class SpiderZXGY:SpiderBase
  5. {
  6. public override string Spider(Utility.Device oDevice)
  7. {
  8. Console.WriteLine("中兴ZXGY设备开始采集");
  9. return "ZXGY";
  10. }
  11. }
  12. }

由于这里采用了接口,我们将ESTM.Spider.Zhongxing这个项目开发完成后生成dll,将dll放到控制台程序中,直接通过MEF导入不同的子类对象就可以使用,不需要更改控制台里面的大部分东西。如果不用接口,而是直接用抽象类代替,那么控制台里面大部分的代码都得改,并且控制台程序依赖多个dll,对设计的松耦合也不利。博主这里为了简单,用了MEF来简单导入,其实正式项目中,应该是用工厂采用反射直接创建出具体的实例。

四、总结

1、接口是一组规则的集合,它主要定义的是事物的规则,体现了是这种类型,你就必须有这些规则的概念。它的目的主要是依赖倒置和松耦合,从这点来说,接口不能省掉或者用抽象类代替。总而言之,接口和抽象类不可同日而语。

2、抽象类主要用于公共实现和约束子类必须重写。以上面的例子说明,Login、Loginout用于公共实现,提高了代码复用,Spider用于抽象,约束子类必须要重写Spider方法。这也就是这里不能用普通类的原因。

3、用一句话概括接口和抽象类的区别:使用抽象类是为了代码的复用,而使用接口的动机是为了实现多态性(依赖倒置)。至于使用的时候到底是用接口还是抽象类,看具体的情况。

C#基础系列——一场风花雪月的邂逅:接口和抽象类的更多相关文章

  1. 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!

    目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...

  2. c#基础系列(转)

    转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...

  3. Java工程师学习指南第1部分:夯实Java基础系列

    点击关注上方"Java技术江湖",设为"置顶或星标",第一时间送达技术干货. 本文整理了微信公众号[Java技术江湖]发表和转载过的Java优质文章,想看到更多 ...

  4. 2015年12月28日 Java基础系列(六)流

    2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流

  5. Java基础系列4:抽象类与接口的前世今生

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 1.抽象类: 当编写 ...

  6. 带你学够浪:Go语言基础系列 - 10分钟学方法和接口

    文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 对于一般的语言使用者来说 ,20% 的语言特性就能够满 ...

  7. linux驱动基础系列--linux spi驱动框架分析

    前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...

  8. 【Basics of Entity Framework】【EF基础系列1】

    EF自己包括看视频,看MSDN零零散散的学了一点皮毛,这次打算系统学习一下EF.我将会使用VS2012来学习这个EF基础系列. 现在看看EF的历史吧: EF版本 相关版本特性介绍 EF3.5 基于数据 ...

  9. C#基础系列——Attribute特性使用

    前言:上篇 C#基础系列——反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术——特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属 ...

随机推荐

  1. Google LOGO现代舞舞蹈动画

    效果预览:http://hovertree.com/texiao/js/5.htm 截图: HTML文件代码: <!DOCTYPE html> <html xmlns="h ...

  2. AlloyRenderingEngine开门大吉

    快速入口 不读文章可以直接拐向这里: github:https://github.com/AlloyTeam/AlloyRenderingEngine website:http://alloyteam ...

  3. 64位win7+vs2010编译.net3.5以前的版本问题

    一般编译会出现 1.“ResGen.exe”已退出,代码为2 问题处理 2.“错误 2 “LC.exe”已退出,代码为 -1. NBGIS.MainGIS” 3.“未能加载文件或程序集“ESRI.Ar ...

  4. Android—锁定横屏遇到的问题

    Android开发应客户需求需要锁定横屏,期间遇到的问题与大家共享一下: 首先在项目清单文件中设置: android:screenOrientation="landscape"// ...

  5. 谈谈iOS app的线上性能监测

    在移动端开发者中最重要的KPI应该是崩溃率.当崩溃率稳定下来后,工作的重心就应该转移到性能优化上.那么问题来了,如果你的项目也没有接入任何性能监测SDK,没有量化的指标来衡量,那你说你优化了性能领导信 ...

  6. iOS切图文件的命名规范

    万能公式:

  7. iOS Swift-简单值(The Swift Programming Language)

    iOS Swift-简单值(The Swift Programming Language) 常量的声明:let 在不指定类型的情况下声明的类型和所初始化的类型相同. //没有指定类型,但是初始化的值为 ...

  8. IOS开发之Bug--iOS7View被导航栏遮挡问题的解决

    在实际开发中,遇到在UITextView的frame等于当前控制器的View的frame的情况下,然后运行的时候,发现控制器的Frame的高度y值会从导航条的位置64变化到0. 导致UITextVie ...

  9. 使用 Async 和 Await 的异步编程(C# 和 Visual Basic)[msdn.microsoft.com]

    看到Microsoft官方一篇关于异步编程的文章,感觉挺好,不敢独享,分享给大家. 原文地址:https://msdn.microsoft.com/zh-cn/library/hh191443.asp ...

  10. Nagios学习实践系列——基本安装篇

    开篇介绍 最近由于工作需要,学习研究了一下Nagios的安装.配置.使用,关于Nagios的介绍,可以参考我上篇随笔Nagios学习实践系列——产品介绍篇 实验环境 操作系统:Red Hat Ente ...