简体   繁体   English

抽象基类与具体类作为超类

[英]Abstract Base Class vs. Concrete Class as a SuperType

After reading the most excellent book "Head First Design Patterns", I began proselytizing to my colleagues the benefits of patterns and design principles. 在阅读了最优秀的“Head First Design Patterns”一书之后,我开始向同事们传播模式和设计原则的好处。 While extolling the virtues of my favorite pattern - Strategy Pattern - I was asked a question which gave me pause. 在颂扬我最喜欢的模式的优点 - 策略模式 - 我被问到一个让我停顿的问题。 Strategy, of course, uses inheritance and composition and I was on one of my tirades about "program to an interface (or supertype) not an implementation", when a colleague asked "why use an abstract base class instead of a concrete class?". 当然,策略使用继承和组合,当我的同事问“为什么使用抽象基类而不是具体的类?”时,我在其中一个关于“程序到接口(或超类型)而不是实现”的长篇大论。 。
I could only come up with "well you force your subclasses to implement abstract methods and prevent them from instantiating the ABC". 我只能提出“你强迫你的子类实现抽象方法并阻止它们实例化ABC”。 But to be honest the question caught me off gaurd. 但说实话,这个问题让我想起了gaurd。 Are these the only benefits of using an abstract base class over a concrete class at the top of my hierarchy? 这些是在我的层次结构顶部使用抽象基类而不是具体类的唯一好处吗?

If you need specific methods to be implemented, then use an Interface. 如果需要实现特定方法,请使用接口。 If there is shared logic that can be pulled out, use an abstract base class. 如果存在可以拉出的共享逻辑,请使用抽象基类。 If the base set of functionality is complete on its own, then you can use a concreate class as the base. 如果基本功能集完全独立,那么您可以使用concreate类作为基础。 An abstract base class, and an Interface cannot be instantiated directly, and that is one of the advantages. 抽象基类和接口不能直接实例化,这是其中一个优点。 If you can use a concrete type, then you need to do override methods, and that has a "code smell" to it. 如果您可以使用具体类型,那么您需要执行覆盖方法,并且它具有“代码味道”。

Program to interface, not to implementation has little to do with abstract and concrete classes. 接口的程序,而不是实现与抽象和具体的类几乎没有关系。 Remember the template method pattern ? 还记得模板方法模式吗? Classes, abstract or concrete, are the implementation details. 类,抽象或具体,是实现细节。

And the reason to use abstract classes instead of concrete classes is that you can invoke methods without implementing them, but by leaving them to be implemented to subclasses instead. 使用抽象类而不是具体类的原因是,您可以在不实现方法的情况下调用方法,而是将它们实现为子类。

Programming to an interface is a different thing - it is defining what your API does, not how it does it. 面向接口编程是一个不同的东西-它是定义你的API做什么 ,它不是怎么做的。 And this is denoted by interfaces. 这由接口表示。

Note one key difference - you can have protected abstract methods, which means that this is implementation detail. 注意一个关键区别 - 您可以使用protected abstract方法,这意味着这是实现细节。 But all interface methods are public - part of the API. 但是所有接口方法都是公共的 - 这是API的一部分。

Yes, although you could also use an interface to force a class to implement specific methods. 是的,虽然您也可以使用接口强制类实现特定方法。

Another reason for using an abstract class as opposed to a concrete class is that an abstract class obviously can't be instantiated. 使用抽象类而不是具体类的另一个原因是抽象类显然无法实例化。 Sometimes you also wouldn't want this to happen, so an abstract class is the way to go. 有时你也不希望这种情况发生,所以抽象类是要走的路。

First of all, the Strategy Pattern should almost never be used in modern C#. 首先,战略模式几乎不应该在现代C#中使用。 It is mainly for languages like Java that don't support function pointers, delegates, or first-class functions. 它主要适用于Java等语言,它们不支持函数指针,委托或一流函数。 You will see it in older versions of C# in interfaces such as IComparer. 您将在IComparer等接口中的旧版C#中看到它。

As for Abstract Base Class vs. Concrete Class, the answer in Java is always "What works better in this situation?" 对于抽象基类与混凝土类,Java中的答案总是“在这种情况下什么更好?” If your strategies can share code, then by all means let them do so. 如果您的策略可以共享代码,那么请务必让他们这样做。

Design patterns are not instructions on how to do something. 设计模式不是关于如何做某事的说明。 They are ways to categorize things that we have already done. 它们是对我们已经完成的事情进行分类的方法。

Abstract base classes are usually used in scenarios where the designer wants to force an architectural pattern where certain tasks are to be carried out in the same manner by all the classes while other behaviours are dependent on the subclasses. 抽象基类通常用于设计者想要强制构造模式的场景,其中所有类以相同的方式执行某些任务,而其他行为依赖于子类。 example: 例:

public abstract class Animal{

public void digest(){

}

public abstract void sound(){

}
}

public class Dog extends Animal{
public void sound(){
    System.out.println("bark");
}
}

Stratergy pattern asks designers to use Compositional behaviour for cases where there are families of alogirthms for a behaviour. Stratergy模式要求设计师在有行为的alogirthms家族的情况下使用Compositional行为。

If the client relies on "implied behavior contract[s]", it is programmed against an implementation and against unguaranteed behavior. 如果客户依赖于“隐含行为合同[s]”,则会根据实施情况和无担保行为进行编程。 Overriding the method while following the contract only exposes bugs in the client, not causes them. 遵循合同时覆盖方法只会暴露客户端中的错误,而不会导致错误。

OTOH, the mistake of assuming contracts that aren't there is less likely to cause problems if the method in question is non-virtual--ie, overriding it cannot cause problems because it cannot be overridden. OTOH,如果所讨论的方法是非虚拟的,那么假设不存在的合同的错误就不太可能导致问题 - 即,覆盖它不会导致问题,因为它不能被覆盖。 Only if the implementation of the original method is changed (while still obeying the contract) can it break the client. 只有当原始方法的实现发生变化(同时仍然遵守合同)时才能破坏客户端。

The question of whether a base class should be abstract or concrete depends IMHO largely on whether a base class object which implemented only behaviors that were common to all objects in the class would be useful. 基类应该是抽象的还是具体的问题在很大程度上依赖于IMHO是否只实现类中所有对象共有的行为的基类对象是有用的。 Consider a WaitHandle. 考虑一下WaitHandle。 Calling "wait" upon it will cause code to block until some condition is satisfied, but there's no common way of telling a WaitHandle object that its condition is satisfied. 对它进行“等待”将导致代码阻塞直到满足某些条件,但是没有通用的方法告诉WaitHandle对象它的条件是否满足。 If one could instantiate a "WaitHandle", as opposed to only being able to instantiate instances of derived types, such an object would have to either never wait, or always wait forever. 如果可以实例化“WaitHandle”,而不是只能实例化派生类型的实例,那么这样的对象必须永远不会等待,或者总是等待永远。 The latter behavior would be pretty useless; 后一种行为将毫无用处; the former might have been useful, but could be achieved almost as well with a statically-allocated ManualResetEvent (I think the latter wastes a few resources, but if it's statically allocated the total resource loss should be trivial). 前者可能有用,但几乎可以通过静态分配的ManualResetEvent实现(我认为后者浪费了一些资源,但如果静态分配,则总资源损失应该是微不足道的)。

In many cases, I think my preference would be to use references to an interface rather than to an abstract base class, but provide with the interface a base class which provides a "model implementation". 在许多情况下,我认为我的偏好是使用对接口的引用而不是对抽象基类的引用,但是为接口提供了一个提供“模型实现”的基类。 So any place one would use a reference to a MyThing, one would supply a reference to "iMyThing". 因此,任何人都会使用MyThing的引用,一个人会提供对“iMyThing”的引用。 It may well be that 99% (or even 100%) of iMyThing objects are actually a MyThing, but if someone ever needs to have an iMyThing object which inherits from something else, one could do so. 很可能99%(甚至100%)的iMyThing对象实际上是一个MyThing,但是如果有人需要拥有一个继承其他东西的iMyThing对象,那么就可以这样做。

Prefer abstract base classes in below scenarios: 首选以下场景中的抽象基类:

  1. A base class can't exist with out a sub class => the base class is simply abstract and it can' t be instantiated. 基类不能与子类一起存在=>基类只是抽象的,不能实例化。
  2. A base class can't have full or concrete implementation of a method => Implementation of a method is base class is incomplete and only sub classes can provide complete implementation. 基类不能具有方法的完整或具体实现=>方法的实现是基类是不完整的,只有子类可以提供完整的实现。
  3. Base class provides a template for method implementation but it still depends on Concrete class to complete the method implementation - Template_method_pattern 基类为方法实现提供了一个模板,但它仍然依赖于Concrete类来完成方法实现 - Template_method_pattern

A simple example to illustrate above points 一个简单的例子来说明上述观点

Shape is abstract and it can't exist without Concrete shape like Rectangle . Shape是抽象的,没有像Rectangle这样的混凝土形状就不能存在。 Drawing a Shape can't be implemented at Shape class since different shapes have different formulas. 由于不同的形状具有不同的公式,因此无法在Shape类中实现绘制Shape The best option to handle scenario : leave draw() implementation to sub-classes 处理场景的最佳选择:将draw()实现留给子类

abstract class Shape{
    int x;
    int y;
    public Shape(int x,int y){
        this.x = x;
        this.y = y;
    }
    public abstract void draw();
}
class Rectangle extends Shape{
    public Rectangle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Rectangle using x and y : length * width
        System.out.println("draw Rectangle with area:"+ (x * y));
    }
}
class Triangle extends Shape{
    public Triangle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Triangle using x and y : base * height /2
        System.out.println("draw Triangle with area:"+ (x * y) / 2);
    }
}
class Circle extends Shape{
    public Circle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Circle using x as radius ( PI * radius * radius
        System.out.println("draw Circle with area:"+ ( 3.14 * x * x ));
    }
}

public class AbstractBaseClass{
    public static void main(String args[]){
        Shape s = new Rectangle(5,10);
        s.draw();
        s = new Circle(5,10);
        s.draw();
        s = new Triangle(5,10);
        s.draw();
    }
}

output: 输出:

draw Rectangle with area:50
draw Circle with area:78.5
draw Triangle with area:25

Above code covers point 1 and point 2. You can change draw() method as template method if base class has some implementation and calls sub-class method to complete draw() function. 上面的代码涵盖了第1点和第2点。如果基类有一些实现并调用子类方法来完成draw()函数,则可以将draw()方法更改为模板方法。

Now same example with Template method pattern: 现在使用Template方法模式的相同示例:

abstract class Shape{
    int x;
    int y;
    public Shape(int x,int y){
        this.x = x;
        this.y = y;
    }
    public abstract void draw();

    // drawShape is template method
    public void drawShape(){
        System.out.println("Drawing shape from Base class begins");
        draw();
        System.out.println("Drawing shape from Base class ends");       
    }
}
class Rectangle extends Shape{
    public Rectangle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Rectangle using x and y : length * width
        System.out.println("draw Rectangle with area:"+ (x * y));
    }
}
class Triangle extends Shape{
    public Triangle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Triangle using x and y : base * height /2
        System.out.println("draw Triangle with area:"+ (x * y) / 2);
    }
}
class Circle extends Shape{
    public Circle(int x,int y){
        super(x,y);
    }
    public void draw(){
        //Draw Circle using x as radius ( PI * radius * radius
        System.out.println("draw Circle with area:"+ ( 3.14 * x * x ));
    }
}

public class AbstractBaseClass{
    public static void main(String args[]){
        Shape s = new Rectangle(5,10);
        s.drawShape();
        s = new Circle(5,10);
        s.drawShape();
        s = new Triangle(5,10);
        s.drawShape();
    }
}

output: 输出:

Drawing shape from Base class begins
draw Rectangle with area:50
Drawing shape from Base class ends
Drawing shape from Base class begins
draw Circle with area:78.5
Drawing shape from Base class ends
Drawing shape from Base class begins
draw Triangle with area:25
Drawing shape from Base class ends

Once you have decided that you have to make as method abstract , you have two options : Either user interface or abstract class. 一旦确定必须将方法作为方法abstract ,您有两个选择:用户interfaceabstract类。 You can declare your methods in interface and define abstract class as class implementing the interface . 您可以在interface声明方法,并将abstract类定义为实现interface类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Guice将抽象基类绑定到具体类 - Guice bind abstract base class to concrete class 使用抽象生成器vs具体类和具体生成器扩展抽象类 - Extending an abstract class with an abstract builder vs concrete class and concrete builder 返回扩展抽象基类的具体类的类,而不是实例? - Return a class, not an instance, of a concrete class that extends an abstract base class? 设计一个抽象基类。 使用什么类型,抽象或具体? - Designing an abstract base class. What types to use, abstract or concrete? 抽象派生类中的抽象方法重写/实现抽象基类的抽象或具体方法。 如何以及为什么? - Abstract method in abstract Derived class overrides/implements abstract or concrete method of abstract Base class. How and why? 抽象 Class 和具体子类 - Abstract Class and Concrete Subclasses 抽象类中的具体方法 - Concrete method in abstract class 具体类中的受保护构造函数与抽象类中的公共构造函数 - protected constructor in concrete class vs public constructor in abstract class 具体类的多个实例与抽象类的多个实现 - Multiple Instances of a concrete class vs multiple implementations of an abstract class 在VS接口中定义的匿名类。 接口外部的具体类 - Anonymous class defined in an interface VS. Concrete class outside of an interface
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM