现在,我正在学习OOP,主要是在c#中。我感兴趣的是创建一个无法实例化的类的主要原因是什么。什么时候制作抽象类的正确例子是什么? 我发现自己在继承方式中使用抽象类太热情了。当类在系统中是抽象的并且类不应该是抽象的时候是否有一些规则? 例如,我以某种方式制作了类似的医生和患者类,所以我从抽象类Person中派生出它们(因为它们都有名字和姓氏)。那是错的吗? 对不起,如果问题很愚蠢,我就是新人。
答案 0 :(得分:2)
到目前为止,还有一些事情没有人指出过,所以我只想指出它们。
您只能从一个基类继承(可能是抽象的),但您可以实现许多接口。因此,在这个意义上,继承抽象类比实现接口更紧密。
因此,如果您后来意识到您需要一个实现两个不同抽象类的类,那么您就会深陷其中:)
要回答你的问题“什么时候做一个抽象课”,我会说永远不要,如果可能的话,避免它,从长远来看它永远不会得到回报,如果主类不适合作为一个普通的类,它可能不是真的需要抽象,使用接口。如果您遇到复制代码的情况,可能适合抽象类,但总是首先查看接口和行为模式(除了策略模式解决了很多问题的人)错误地使用继承来解决,总是喜欢组合而不是继承)。使用抽象类作为最后的解决方案,而不是设计。
为了更好地理解OOP,我建议您查看Design Patterns: Elements of Reusable Object-Oriented Software (a book),它可以很好地概述OO设计和OO组件的可重用性。 OO设计不仅仅是继承:)
答案 1 :(得分:1)
例如:您需要从不同来源提取数据,例如“Excel文件,XML,任何数据库等”并保存在一个公共目标中。它可以是任何数据库。所以在这种情况下你可以使用这样的抽象类。
abstract class AbstractImporter
{
public abstract List<SoldProduct> FetchData();
public bool UploadData(List<SoldProduct> productsSold)
{
// here you can do code to save data in common destination
}
}
public class ExcelImporter : AbstractImporter
{
public override List<SoldProduct> FetchData()
{
// here do code to get data from excel
}
}
public class XMLImporter : AbstractImporter
{
public override List<SoldProduct> FetchData()
{
// here do code to get data from XML
}
}
public class AccessDataImporter : AbstractImporter
{
public override List<SoldProduct> FetchData()
{
// here do code to get data from Access database
}
}
并且可以像这样打电话
static class Program
{
static void Main()
{
List<SoldProduct> lstProducts;
ExcelImporter excelImp = new ExcelImporter();
lstProducts = excelImp.FetchData();
excelImp.UploadData(lstProducts);
XMLImporter xmlImp = new XMLImporter ();
lstProducts = xmlImp.FetchData();
xmlImp.UploadData(lstProducts);
AccessDataImporterxmlImp accImp = new AccessDataImporter();
lstProducts = accImp .FetchData();
accImp.UploadData(lstProducts);
}
}
因此,在上面的示例中,数据导入功能的实现在扩展(派生)类中分开,但数据上载功能对所有人都是通用的。
答案 2 :(得分:0)
本质上你做的很好,如果你不想实例化一个Person类,但是因为我猜你可能想在将来某个时候实例化一个Person类,所以它不应该是抽象的。
虽然有一个论点是您编写代码来解决当前问题,但不是为了解决可能永远不会出现的问题,因此如果您需要实例化Person类,请不要将其标记为抽象。
抽象类是不完整的,必须在派生类中实现...一般来说,我倾向于更喜欢接口上的抽象基类。
了解抽象类和接口之间的区别......
“抽象类和接口之间的区别在于抽象类可以具有方法的默认实现,因此如果不在派生类中重写它们,则使用抽象基类实现。接口不能具有任何实施。“取自this SO post
答案 3 :(得分:0)
如前所述,没有人会强迫你使用抽象类,它只是一种抽象某些功能的方法,这种功能在许多类中很常见。
您的案例是使用抽象类的一个很好的示例,因为您在两种不同类型之间具有共同属性。但是因为它限制了你自己使用Person作为一个类型。如果你想要这个限制基本上取决于你。
一般情况下,我不会像你一样使用类似类的抽象类,除非你想要阻止Person被实例化。
通常我使用抽象类,如果我也定义了一个接口,我需要为这个接口编写不同的实现,但是也想要一个已经涵盖所有实现的一些常用功能的BaseClass。
答案 4 :(得分:0)
从抽象类'Person'中获取'Doctor'和'Patient'都很好,但是你应该将Person设为普通类。但这取决于使用“人物”的背景。
例如,您可能有一个名为“GameObject”的抽象类。游戏中的每个对象(例如Pistol,OneUp)都扩展了“GameObject”。但是你不能单独拥有一个'GameObject',因为'GameObject'描述了一个类应该有什么,但是没有详细说明它们是什么。
例如,游戏对象可能会说:“所有游戏对象必须看起来像某样东西。”手枪可能延伸到游戏对象所说的内容,并且它说“所有手枪必须看起来像一个长长的枪管,一端有一个握把触发“。
答案 5 :(得分:0)
这可能是一个非学术定义,但抽象类应该代表一个如此“抽象”的实体,实例化它是没有意义的。
它通常用于创建必须由具体类扩展的“模板”。因此抽象类可以实现共同的功能,例如实现一些接口的方法,一个特定行为的具体类实现的委托。
答案 6 :(得分:0)
关键是该类的实例化是否有意义。如果它永远不适合实例化那个类,那么它应该是抽象的。
一个典型的例子是Shape基类,包含Square,Circle和Triangle子类。永远不应该实例化Shape,因为根据定义,您不知道您希望它是什么形状。因此,使Shape成为一个抽象类是有意义的。
答案 7 :(得分:0)
顺便提一下,另一个尚未提及的问题是,可以将成员添加到抽象类,让现有实现自动支持它们,并允许消费者使用了解新成员和实现的实现。不可互换。虽然有一些似乎合理的机制,未来的.NET运行时可以通过这种方式允许接口以这种方式工作,但目前他们没有。
例如,如果IEnumerable是一个抽象类(当然有很多原因导致它没有),那么当它的用处变得明显时,就可以添加类似Count
方法的东西。它的默认实现Count
可能与IEnumerable<T>.Count
扩展方法非常相似,但了解新方法的实现可以更有效地实现它(尽管IEnumerable<T>.Count
会尝试利用它的实现ICollection<T>.Count
或ICollection.Count
,首先必须确定它们是否存在;相反,任何覆盖都知道它有代码直接处理Count
。< / p>
可以添加一个ICountableEnumerable<T>
接口,该接口继承自IEnumerable<T>
但包含Count
,现有代码可以继续与IEnumerable<T>
一起使用,因为它总是有,但是只要ICountableEnumerable<T>
通过现有代码传递,收件人就必须将其重新转换为ICountableEnumerable<T>
以使用Count
方法。使用直接发送Count
方法直接在IEnumerable<T>
[{1}}扩展方法上行动的方法并不太方便,但它的效率远低于直接-dispatched virtual method]。
如果存在接口可以包含静态方法的方法,并且如果类加载器在发现声称实现Count
的类Boz
时缺少方法{{1} },会自动添加到该类:
IFoo
[假设接口包含该静态方法],那么可以让接口添加成员而不破坏现有实现,只要它们还包含可以由默认实现调用的静态方法。不幸的是,我知道没有计划添加任何此类功能。