Can you help me understand in a practical example the usage abstract classes vs interfaces?
你能让我对抽象类和继承的使用有一个近乎过于简单的理解,并帮助我真正理解这个概念以及如何实现吗?我有一个我正在努力完成的项目,却对如何实现这个项目迷路了。我一直在和我的教授聊天,被告知了很多,说如果我搞不清楚,我可能还没准备好上这门课。我已经克服了预请求者的课程,仍然难以理解这些概念。
澄清一下,我目前所做的项目如下。我还没把狗/猫的课程等填好。你能给我一个指针吗?我不是要任何人给我"答案",我只是迷路了去哪里。我上过网络课程,他和我的沟通工作一直很麻烦。我刚刚完成了所有其他课程的4.0,所以我愿意付出努力,但我对这些概念的理解和如何实际应用这些概念的理解却迷路了。
有什么意见或帮助能让我在这个项目上取得进一步进展吗?
我要实现的内容描述如下:
Overview:
The purpose of this exercise is to
demonstrate the use of Interfaces,
Inheritance, Abstract classes, and
Polymorphism. Your task is to take
the supplied program shell and ADD the
appropriate classes and corresponding
class members/methods to get this
program to function correctly. You may
not make changes to any of the code
supplied, you may only add the classes
you write. Although there are
numerous ways to get the program
working, you must use techniques that
demonstrate the use of Interfaces,
Inheritance, Abstract classes, and
Polymorphism. Again, to make clear,
you can add to the supplied code but
you cannot change or delete any of
it. The code that is supplied will
work with very little additional code
and will satisfy the requirements of
the exercise.If you successfully complete the
assignment, your program should output
the following statements when run:My name is Spot, I am a Dog
My name is Felix, I am a Cat
Requirements:
1) You must have an abstract base
class called 'Animal' from which the
Dog and Cat classes derive.2) The Animal base class must derive
from the Interface 'IAnimal', it is
the only class that should derive from
IAnimal.3) Since all animals have a name and a
name is not an attribute that is
specific to a dog or a cat, the Animalbase class should be where the name
is stored and where the WhatIsMyName
get-property is implemented.4) You will need to create a Dog and a
Cat class that will derive only from
the Animal base class.5) The Dog and Cat classes should
implement the WhatAmI get-property and
return the appropriate string value.
号
无法更改的代码:
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 | using System; namespace IT274_U2 { public interface IAnimal { string WhatAmI { get; } string WhatIsMyName { get; } } public class TesterClass { public static void DescribeAnimal(IAnimal animal) { Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI); } static void Main(string[] args) { Dog mydog = new Dog("Spot"); Cat mycat = new Cat("Felix"); DescribeAnimal(mydog); DescribeAnimal(mycat); } } } |
///////////////////////
到目前为止我写的代码:
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 | using System; namespace IT274_U2 { public interface IAnimal { string WhatAmI { get; } string WhatIsMyName { get; } } public class Dog { public abstract string WhatAmI { get; set; } }//end public class Dog public class Cat { public abstract string WhatIsMyName { get; set; } }//end public class Cat public abstract class Animal : IAnimal { // fields protected string Dog; protected string Cat; // implement WhatIsMyName //properties public abstract String Dog { get; set; } public abstract String Cat { get; set; } public abstract string WhatIsMyName(); } //end public abstract class Animal public class TesterClass { public static void DescribeAnimal(IAnimal animal) { Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI); } static void Main(string[] args) { Dog mydog = new Dog("Spot"); Cat mycat = new Cat("Felix"); DescribeAnimal(mydog); DescribeAnimal(mycat); } } } |
号
编辑:
我已经把每节课的代码体取出来了-如果你想看我的答案,请看一下编辑版本:)
首先,我们定义接口
1 2 3 4 5 | public interface IAnimal { string WhatAmI { get; } string WhatIsMyName { get; } } |
实现此接口的任何类都必须实现这些属性。接口就像契约;实现接口的类同意提供接口方法、属性事件或索引器的实现。
接下来,我们需要定义抽象的动物类
1 2 3 4 | public abstract class Animal : IAnimal { //Removed for Training, See Edit for the code } |
号
类是抽象的这一事实表明该类只打算作为其他类的基类。我们已经实现了接口的两个属性,并且还拥有一个用于存储动物名称的私有字段。此外,我们还抽象了
现在,让我们定义我们的
1 2 3 4 5 6 7 8 9 | public class Dog : Animal { //Removed for Training, See Edit for the code } public class Cat : Animal { //Removed for Training, See Edit for the code } |
这两个类都继承自
其余的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Program { public static void DescribeAnimal(IAnimal animal) { Console.WriteLine("My name is {0}, I am a {1}", animal.WhatIsMyName, animal.WhatAmI); } static void Main(string[] args) { Dog mydog = new Dog("Spot"); Cat mycat = new Cat("Felix"); DescribeAnimal(mydog); DescribeAnimal(mycat); Console.ReadKey(); } } |
。
静态方法
由于
我希望我已经清楚地解释了这一点,如果有人认为我的选择需要加强,请发表评论,我将很乐意编辑我的答案。
接口和抽象类的主要区别在于,在接口中,您只定义实现此接口的对象的公共方法和属性。抽象类提供一些基本实现,但有一些"间隙"——继承者需要实现的抽象方法。
我不会帮你做家庭作业,但要提醒你:动物课不应该包含任何针对猫狗的内容。
你很接近,但这比需要的要难。
我不想给你答案;)但这里有一些建议。
首先,创建3个类和1个接口。但是,我认为您可能缺少的一件事是您需要3种不同类型的对象(从"定义最少"到"定义最多"):
1)接口这是iAnimal,可以通过任何可以像动物一样行动的东西来实现。
2)抽象基类这是动物的钙质——任何动物的钙质都应该来自动物,但这些不能直接创造。如果你假装你是上帝,你不会做动物,你会做狗、猫、松鼠或毛绒布。
3)动物具体实施这些是实际的类本身。这就是你所创造的。狗或猫在你的情况下。
这里的技巧是,您只能创建具体的类,但是您可以使用iAnimal或Animal(接口或抽象基类)来操纵和处理任何动物(或者,在接口的情况下,任何类似动物的行为)。
一般来说:
接口描述对象将响应的方法。这是一个目标承诺满足的合同。
抽象类描述基本的功能,并让专门的功能成为子类。
所以,基本上,当您希望对象在性质上不同时,使用一个接口,响应相同的特定方法。
当需要某个类的专门版本时,可以使用抽象类。
假设您想要创建一个系统,其中任何类型的对象都可以由唯一的ID标识,而您不关心它们所属的类。
您可能有:
动物
运输
计算机小工具。
无论什么。
由于它们是不相关的主题,您可以选择实现和接口,比如:
1 2 3 4 5 | public interface IIdentifiable { public long GetUniqueId(); } |
所有想要满足这个契约的类都将实现这个接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class IPod: IIdentifiable { public long GetUniqueId() { return this.serialNum + this.otherId; } } public class Cat: IIdentifiable { public long GetUniqueId() { return this..... } } |
号
ipod和cat的性质都非常不同,但它们都可能响应将在目录系统中使用的"getUniqueID()"方法。
然后可以这样使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
另一方面,您可能有一个抽象类来定义对象可能具有的所有公共行为,并让子类定义专门的版本。
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 | public abstract class Car { // Common attributes between different cars private Tires[] tires; // 4 tires for most of them private Wheel wheel; // 1 wheel for most of them. // this may be different depending on the car implementation. public abstract void move(); } class ElectricCar: Car { public void move() { startElectricEngine(); connectBattery(); deploySolarShields(); trasnformEnertyToMovemetInWheels(); } } class SteamCar: Car { public void move() { fillWithWather(); boilWater(); waitForCorrectTemperature(); keepWaiting(); releasePreasure.... } } |
。
在这里,两种汽车,以不同的方式实现了"移动"方法,但它们在基类中仍然有共同点。
为了使事情更有趣,这两个汽车也可以实现可识别的接口,但通过这样做,他们只是承诺响应getuniqueid方法,而不是汽车的本质。这就是为什么它自己的汽车不能实现这个接口的原因。
当然,如果标识可能基于cars可能具有的公共属性,则getIdentifiableID可以由基类实现,子类将继承该方法。
//案例1…每个子类实现接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class ElectricCar: Car, IIdentifiable { public void move() { ..... } public long GetUniqueId() { .... } } public class SteamCar: Car, IIdentifiable { public void move() { ..... } public long GetUniqueId() { .... } } |
实例2,基类实现了接口,子类从中受益。
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 | public abstract class Car: IIdentifiable { // common attributes here ... ... ... public abstract void move(); public long GetUniqueId() { // compute the tires, wheel, and any other attribute // and generate an unique id here. } } public class ElectricCar: Car { public void move() { ..... } } public class SteamCar: Car { public void move() { ..... } } |
。
我希望这有帮助。
老实说,不管这是不是家庭作业问题,这个行业有多少人不知道这一点,这让我感到害怕。所以我会回答。
接口抽象实现,抽象类也一样。没有"vs",因为您也可以创建一个实现接口的抽象类。所以别以为他们在互相打仗。
因此,当您不希望消费者对实现了解太多时,可以使用这两种方法。一个接口在这项工作中做得更好,因为它没有实现,它只说明了消费者可以按返回的值并发送的按钮,抽象类的状态可能比这个(甚至更多!)如果你把这一点带到船上,你只需要接口。Ergo,第二点:
当您想要在一个接口的两个不同实现之间共享公共代码时,可以使用抽象类。在这个场景中,两个具体实现都继承自实现接口的抽象类。
简单的例子是IDataStore。SavingToatextFile数据存储只是一个实现IDataStore的类。但是,mssqldatastore和mysqldatastore将共享公共代码。它们都将从实现IDataStore的抽象类sqldatastore继承。
接口是一种契约。这里是您想要描述您将提供的功能的地方,没有任何实现细节。
抽象类是一个类,其目的是在其子类之间共享实现细节。因为它只用于代码共享/分解,所以不能实例化
您的实际类将从抽象类继承,并在必要时使用抽象类中共享的代码实现其特定于类的功能。
另一个建议(稍微偏离主题,但仍然相关)
我建议出于学习目的,避免自动属性。如果您明确地实现它们,它将帮助您了解正在发生的事情。
例如,不执行以下操作:
1 2 3 4 5 6 7 8 | class SomeClass { public string MyProperty { get; set; } } |
尝试自己实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class SomeClass { public string MyProperty { get { return"MyValue"; // Probably a private field } set { // myField = value; or something like that } } |
号
我之所以提到这一点,是因为它将在这个特定的案例中对您有所帮助。因为您使用的是自动属性,所以编译器为您"填充空白",在您的例子中,我认为它阻止了您获得一些非常有用的编译器错误。当试图理解这些概念是如何工作的时,你自己做的工作通常会使它更容易,而不是更难。
基本上,接口定义了所有实现者必须提供的"契约"(即一组操作/属性)。在这种情况下,IAnimal接口需要whatami和whatismyname属性。抽象类可以提供某些功能,但也会留下一些必须由其子类实现的操作。
在您的示例中,动物基类能够提供whatismyname功能,因为"name"是所有动物的属性。但是,它不能提供"whatami"属性,因为只有特定的子类知道它们是什么"类型"。
您发布的示例代码的问题在于,thet-animal类知道它的子类,而它不应该知道它的子类。
抽象类:为派生类建立一个基础,它们为所有派生类提供一个契约。它强制继承遗产
接口:
接口不是类,而是方法的定义。
一个类可以插入多个接口,但只能插入一个抽象类。
希望能帮上忙