What is an abstract class?
当我学习抽象类时,我会说wt(h*)!!!!
问题:
**如果你知道我的意思*
例如:
1 | public abstract class Stream { /* lots of code, some abstract methods */ } |
一条小溪究竟是什么?什么样的小溪?文件流?一个网络?内存缓冲区?每一种可能都有不同的和不相关的读/写方式,但提供了一个通用的API。仅仅创建一个
1 2 3 | Stream s = CreateStream(...); // I don't *care* what kind of stream s.Write(new byte[] {1,2,3,4,5}); s.Close(); |
抽象(基础)类为您提供半具体的类来实现类层次结构。它们允许你做几件事:
名单还在继续。
1)创建一个不能实例化的类有什么意义?
仅仅因为某个东西没有被直接实例化并不意味着它是有用的。抽象类在继承中起着重要的作用,在继承类的类设计中非常有用。
例如,我以前使用抽象类来定义类必须符合的基本结构。然后,我已经基于抽象类定义了继承的类,如果我遗漏了一个必需的方法或其他东西,编译器就可以选择它。
这还允许您将继承的类关联在一起,这意味着您可以假定抽象类中定义的某些方法将存在于继承的类中——这可能很有用。
2)为什么会有人想要这样的课程?
通常,我使用它来确保一系列继承类具有某些方法等。对我来说,它对于一组继承类的结构的设计非常有用。
3)抽象类在什么情况下变得必要?
我认为抽象类从来都不是必需的,但是在某些情况下使用它可以帮助简化您正在解决的问题。
它只是为了向使用它的客户机隐藏实际的实现。
- 它为某些功能提供了concerte实现,并且不能直接实例化。
- 这只能从实现它的类中访问。
- 因此,使用派生类的客户机永远不会知道功能的实现,因为它是弃权的。
现在您会问为什么我们需要这个,因为接口服务于相同的机制。浏览简单的记录器示例
1 2 3 4 5 6 | interface ILogger { string PrepareLog(System.Exception ex); void InitializeLogger(string Type); int WriteLog(string msg); } |
任何实现此接口的日志客户端都应该实现所有这些功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | class EventLogger : ILogger { public override void InitializeLogger(string Type) { //Event Logger Initialize } public override int WriteLog(string msg) { //Write to event log return 1; } public override string PrepareLog(System.Exception ex) { return ex.StackTrace ; } } class FileLogger : ILogger { public override void InitializeLogger(string Type) { } public override int WriteLog(string msg) { //Write to File return 1; } public override string PrepareLog(System.Exception ex) { return ex.StackTrace ; } } class MailLogger : ILogger { public override void InitializeLogger(string Type) { } public override int WriteLog(string msg) { //Write to mail return 1; } public override string PrepareLog(System.Exception ex) { //prepare HTML Formatted msg return ex.StackTrace ; } } |
类eventlogger、filelogger和maillogger实现了ilogger,并给出了上下文特定的实现。现在我们想要隐藏PrepareLog的实际实现,这将完成从异常对象准备日志消息的常见操作。
在我们当前的实现中,我们没有选择使单个方法具体化,而其他方法只是契约。
所以让我们用抽象类稍微改变一下实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | abstract class AbstractLogger:ILogger { #region ILogger Members public virtual string PrepareLog(System.Exception ex) { return ex.StackTrace; } public abstract void InitializeLogger(string Type); public abstract int WriteLog(string msg); #endregion } class EventLogger : AbstractLogger { public override void InitializeLogger(string Type) { //Event Logger Initialize } public override int WriteLog(string msg) { //Write to event log return 1; } } class FileLogger : AbstractLogger { public override void InitializeLogger(string Type) { } public override int WriteLog(string msg) { //Write to File return 1; } } class DBLogger : AbstractLogger { public override void InitializeLogger(string Type) { } public override int WriteLog(string msg) { //Write to DB return 1; } } class MailLogger : AbstractLogger { public override void InitializeLogger(string Type) { } public override int WriteLog(string msg) { //Write to mail return 1; } public override string PrepareLog(System.Exception ex) { //prepare HTML Formatted msg return ex.StackTrace ; } } |
现在,我已经创建了AbstractLogger类,它继承了ILogger并单独实现了PrepareLog方法,Remainig方法保持了抽象。因此,使用者将在其实现中编写特定于上下文的代码。
因此,现在PrepareLog方法完全隐藏(即日志准备)了cosumer,初始化了任何记录器。
那么为什么PrepareLog是虚拟的呢??
有些情况下,使用者可能希望重写PrepareLog方法,例如:mailLogger将重写PrepareLog并格式化HTML格式的输出,以提供给邮件消息。
抽象类是无法实例化的类。例如,正方形、圆形或矩形是一种形状,可以从类形状派生。
形状将包含方形、圆形或矩形所共有的代码,例如计算形状的面积。但是实例化一个形状是没有用的,因为它是一个抽象概念,而正方形、圆形和矩形是真实的实体。
抽象类只有在使用继承时才有用。您创建的子类必须实现与抽象类相同的接口,并且将继承您在抽象类中定义的一些基本实现。
重点是指定派生类必须实现的方法,与接口相同,但也提供一些实现(接口不能实现)。严格地说,抽象类从来不是"必要的",但它们是有用的。只需在.NET或Java标准库中搜索它们,看看它们是如何使用的。你会发现有很多例子。
接口和抽象类都促进了代码基中的松耦合。抽象类是接口和具体类之间的折衷,因为抽象类可以有实际的方法来实现行为。
一般来说,首选接口。当继承树具有子类要使用的公共操作时,抽象类很有用。但即使在这里,您也可能希望声明抽象类实现了一个接口。JoshBloch称之为"抽象接口"模式。这允许您部署甚至是抽象类的不同实现,矛盾的是,抽象类实际上并不是完全抽象的——只有接口是抽象的。
抽象类的要点是定义(限制)接口,而不描述实现。
抽象类可以通过构造具有派生类类型的兼容对象来实例化。
实现一个干净的接口,隐藏丑陋的平台特定代码。同时也为了隐藏私密处以防暴露。(这样才能真正地强制您使用带有抽象接口的类。)
当同一个类有两个完全不同的实现时,这是必要的。想想一个文件、一个套接字和一块内存。所有这些都可以使可读数据可用——使用抽象类,您可以用三种不同的方式实现这三种数据,即使使用代码(调用站点)是以一种方式编写的,以支持这三种数据。
抽象类是一种抽象。它保证行为存在,但不强制行为的实现方式。这使您可以自由地更改行为的实现方式,如果您以后决定这样做的话。
抽象就像键盘、监视器或手机。所有的键盘都能输入数据;所有的显示器都能显示像素;所有的手机都能打电话。但是这些产品的制造商有不同的方法来实现这种行为。
所以当你想打电话的时候,你几乎可以用任何一部手机来打,因为所有的手机制造商都会根据手机的共同抽象概念来制造手机。如果你已经学会了如何在黑莓或LG上打电话,你不需要再学习如何打电话给三星。
手机是手机,抽象类的子类都是抽象类。
与接口一样,抽象类是一种契约,在该契约中,您提供了一些适用于许多场景的通用(或抽象)功能,但希望实现者为每个场景提供一些特定和/或不同的功能。有人提到了
抽象类通过能够创建一个包含概念而不是具体内容的类来达到一个有用的目的。例如,动物可以是一个阶级,但没有人只是一种动物,它或者是一只鸟、狗或猫等,这是不同种类的动物。希望这有帮助。它还与继承和多态等概念结合在一起。
好了,现在您已经创建了一个接口,其中包含了在每个实现中发生更改的所有方法。在编程时,您会注意到一些代码块由接口的所有实现共享。
这些代码块应该放在抽象类中,而不是在每个实现中重复。这样,当某些东西发生变化时,您只需要在抽象类中而不是在每个实现中修复代码。
它只是抽象的,因为您希望所有继承类都有自己的实现。