简体   繁体   English

有关继承和可扩展性的通用OO问题

[英]General OO question about inheritance and extensibility

So, in a single parent inheritance model what's the best solution for making code extensible for future changes while keeping the same interface (I'd like to emphasize the fact that these changes cannot be known at the time of the original implementation , the main focus of my question is to explore the best mechanism/pattern for supporting these changes as they come up )? 因此,在单父继承模型中,使代码可扩展以用于将来的更改同时保持相同的接口的最佳解决方案是什么(我想强调一下这样的事实,即这些更改在原始实现时是未知的 ,主要重点我的问题是探索在这些变化出现时支持这些变化的最佳机制/模式)? I know that this is a very basic OO question and below I provide example of how I've been going about it, but I was wondering if there a better solution to this common problem. 我知道这是一个非常基本的面向对象问题,下面我提供了有关此问题的示例,但我想知道是否有更好的解决方案来解决这一常见问题。

Here's what I've been doing (the example code is in Java): 这是我一直在做的(示例代码在Java中):

In the beginning , the following two classes and interface are created: 首先 ,将创建以下两个类和接口:

public class Foo
{
    protected int z;
}

public interface FooHandler
{
    void handleFoo(Foo foo);
}

public class DefaultFooHandler implements FooHandler
{
    @Override
    public void handleFoo(Foo foo)
    {
        //do something here
    }
}

The system uses variables/fields of type FooHandler only and that object (in this case DefaultFooHandler) is created in a few, well-defined places (perhaps there's a FooHandlerFactory) so as to compensate for any changes that might happen in the future. 系统仅使用FooHandler类型的变量/字段,并且该对象(在本例中为DefaultFooHandler)在几个定义明确的位置(也许有一个FooHandlerFactory)创建,以补偿将来可能发生的任何更改。

Then, at some point in the future a need to extend Foo arises to add some functionality. 然后, 在将来某个时候,需要扩展Foo以添加一些功能。 So, two new classes are created: 因此,创建了两个新类:

public class ImprovedFoo extends Foo
{
    protected double k;
}

public class ImprovedFooHandler extends DefaultFooHandler
{
    @Override
    public void handleFoo(Foo foo)
    {
        if(foo instanceof ImprovedFoo)
        {
            handleImprovedFoo((ImprovedFoo)foo);
            return;
        }
        if(foo instanceof Foo)
        {
            super.handleFoo(foo);
            return;
        }
    }

    public void handleImprovedFoo(ImprovedFoo foo)
    {
        //do something involving ImprovedFoo
    }
}

The thing that makes me cringe in the example above is the if-statements that appear in ImprovedFooHandler.handleFoo 在上面的示例中让我畏缩的是出现在ImprovedFooHandler.handleFoo FooHandler.handleFoo中的if-statements

Is there a way to avoid using the if-statements and the instanceof operator? 有没有一种方法可以避免使用if-statementsinstanceof运算符?

First of all the code you wrote won't work. 首先,您编写的代码无效。 Each time you see instanceof and if...else together be very careful. 每次看到instanceofif...else在一起时,请务必非常小心。 The order of these checks is very important. 这些检查的顺序非常重要。 In your case you'll never execute handleImpovedFoo . 就您而言,您将永远不会执行handleImpovedFoo Guess why :) 猜猜为什么 :)

It's absolutely normal you have these instanceof statements. 您拥有这些instanceof语句是绝对正常的。 Sometimes it's the only way to provide different behavior for a subtype. 有时,这是为子类型提供不同行为的唯一方法。 But here you can use another trick: use simple Map . 但是这里您可以使用另一个技巧:使用简单的Map Map classes of foo-hierarchy to instances of fooHandler-hierarchy. 将foo层次结构的类映射到fooHandler层次结构的实例。

Map<Class<? extends Foo>, FooHandler> map ...

map.put( Foo.class, new FooHandler() );
map.put( ImprovedFoo.class, new ImprovedFooHandler() );

Foo foo ...; // here comes an unknown foo 

map.get( foo.getClass() ).handleFoo( foo );

The best way of handling this depends too much on the individual case to provide a general solution. 处理此问题的最佳方法在很大程度上取决于个别情况,无法提供一般的解决方案。 So I'm going to provide a number of examples and how I would solve them. 因此,我将提供一些示例以及如何解决它们。

Case 1: Virtual File System 情况1:虚拟文件系统

Clients of your code implement virtual file systems which enable them to operate any sort of resource which can be made to look like a file. 您的代码的客户端实现了虚拟文件系统,使它们能够操作任何看起来像文件的资源。 They do so by implementing the following interface. 他们通过实现以下接口来实现。

interface IFolder
{
     IFolder subFolder(String Name);
     void delete(String filename);
     void removeFolder(); // must be empty
     IFile openFile(String Name);
     List<String> getFiles();
}

In the next version of your software you want to add the ability to remove a directory and all it contents. 在您的软件的下一版本中,您要添加删除目录及其所有内容的功能。 Call it removeTree. 将其称为removeTree。 You cannot simply add removeTree to IFolder because that will break all users of IFolder. 您不能简单地将removeTree添加到IFolder中,因为这将破坏IFolder的所有用户。 Instead: 代替:

interface IFolder2 implements IFolder
{
     void removeTree();    
}

Whenever a client registers an IFolder (rather then IFolder2), register 每当客户端注册IFolder(而不是IFolder2)时,都要注册

new IFolder2Adapter(folder)

Instead, and use IFolder2 throughout your application. 而是在整个应用程序中使用IFolder2。 Most of your code should not be concerned with the difference about what old versions of IFolder supported. 您的大多数代码都不必担心IFolder支持哪些旧版本。

Case 2: Better Strings 情况2:更好的字符串

You have a string class which supports various functionality. 您有一个支持各种功能的字符串类。

class String
{
     String substring(int start, end);
}

You decide to add string searching, in a new version and thus implement: 您决定以新版本添加字符串搜索,从而实现:

class SearchableString extends String
{
    int find(String);
}

That's just silly, SearchableString should be merged into String. 太傻了,应该将SearchableString合并到String中。

Case 3: Shapes 情况3:形状

You have a shape simulation, which lets you get the areas of shapes. 您有一个形状模拟,可让您获得形状的面积。

class Shape
{
    double Area();
    static List<Shape> allShapes; // forgive evil staticness
}

Now you introduce a new kind of Shape: 现在,您介绍一种新的Shape:

class DrawableShape extends Shape
{
    void Draw(Painter paint);
}

We could add a default empty Draw method to Shape. 我们可以向Shape添加一个默认的空Draw方法。 But it seems incorrect to have Shape have a Draw method because shapes in general aren't intended to be drawn. 但是,让Shape具有Draw方法似乎是不正确的,因为通常不打算绘制形状。 The drawing really needs a list of DrawableShapes not the list of Shapes that is provided. 该图形确实需要一个DrawableShapes列表,而不是所提供的Shapes列表。 In fact, it may be that DrawableShape shouldn't be a Shape at all. 实际上,可能DrawableShape根本不应该是Shape。

Case 4: Parts 案例4:零件

Suppose that we have a Car: 假设我们有一辆车:

class Car
{
    Motor getMotor();
    Wheels getWheels();
}

void maintain(Car car)
{
    car.getMotor().changeOil();
    car.getWheels().rotate();
}

Of course, you know somewhere down the road, somebody will make a better car. 当然,您知道在未来的某个地方,会有人制造出更好的汽车。

class BetterCar extends Car
{
    Highbeams getHighBeams();
}

Here we can make use of the visitor pattern. 在这里,我们可以利用访客模式。

void maintain(Car car)
{
     car.visit( new Maintainer() );
}

The car passes all of its component parts to calls into ICarVisitor interface allowing the Maintainer class to maintain each component. 汽车将其所有零部件传递给ICarVisitor接口的调用,从而允许Maintenanceer类维护每个零部件。

Case 5: Game Objects We have a game with a variety of objects which can be seen on screen 情况5:游戏对象我们有一款游戏,其中包含可以在屏幕上看到的各种对象

class GameObject
{
   void Draw(Painter painter);
   void Destroy();
   void Move(Point point);
}

Some of our game objects need the ability to perform logic on a regular interval, so we create: 我们的一些游戏对象需要能够定期执行逻辑,因此我们创建:

class LogicGameObject extends GameObject
{
    void Logic();
}

How do we call Logic() on all of the LogicGameObjects? 我们如何在所有LogicGameObjects上调用Logic()? In this case, adding an empty Logic() method to GameObject seems like the best option. 在这种情况下,向GameObject添加一个空的Logic()方法似乎是最好的选择。 Its perfectly within the job description of a GameObject to expect it to be able to know what to do for a Logic update even if its nothing. 完全可以在GameObject的工作描述中看到它,即使它什么都没有,也知道如何进行逻辑更新。

Conclusion 结论

The best way of handling this situations depends on the individual situation. 处理这种情况的最佳方法取决于个人情况。 That's why I posed the question of why you didn't want to add the functionality to Foo. 这就是为什么我提出了一个问题,为什么您不想将功能添加到Foo中。 The best way of extending Foo depends on what exactly you are doing. 扩展Foo的最佳方法取决于您到底在做什么。 What are you seeing with the instanceof/if showing up is a symptom that you haven't extended the object in the best way. 您看到的instanceof / if出现的现象是您没有以最佳方式扩展对象。

Yes, don't violate LSP which is what you appear to be doing here. 是的,不要违反LSP,这就是您在此处所做的。 Have you considered the Strategy pattern? 您是否考虑过战略模式?

In situations like this I usually use a factory to get the appropriate FooHandler for the type of Foo that I have. 在这种情况下,我通常使用工厂来获取适合我所拥有的Foo类型的FooHandler。 In this case there would still be a set of ifs but they would be in the factory not the implementation of the handler. 在这种情况下,仍然会有一组ifs,但是它们将在工厂中而不是处理程序的实现。

This looks like a plain simple case for basic polymorphism.Give Foo a method named something like DontWorryI'llHandleThisMyself() (um, except without the apostrophe, and a more sensible name). 这看起来像是基本的多态性的简单案例。给Foo一个名为DontWorryI'llHandleThisMyself()之类的方法(除了无撇号,还有一个更明智的名称)。 The FooHandler just calls this method of whatever Foo it's given. FooHandler只是调用给定的Foo都调用此方法。 Derived classes of Foo override this method as they please. Foo的派生类根据需要覆盖此方法。 The example in the question seems to have things inside-out. 问题中的示例似乎使事情由内而外。

With the visitor pattern you could do something like this, 利用访客模式,您可以执行以下操作,

abstract class absFoo {}
class Foo extends absFoo
{
    protected int z;

}
class ImprovedFoo extends absFoo
{
    protected double k;

}
interface FooHandler {
    void accept(IFooVisitor visitor, absFoo foo);
}
class DefaultFooHandler implements FooHandler
{
    public void accept(IFooVisitor visitor, absFoo foo)
    {
        visitor.visit(this, foo);
    }
    public void handleFoo(absFoo foo) {
        System.out.println("DefaultFooHandler");
    }
 }
class ImprovedFooHandler implements FooHandler
{
    public void handleFoo(absFoo foo)
    {
        System.out.println("ImprovedFooHandler");
    }

    public void accept(IFooVisitor visitor, absFoo foo) {
        visitor.visit(this, foo);
    }

}

interface IFooVisitor {
    public void visit(DefaultFooHandler fooHandler, absFoo foo);
    public void visit(ImprovedFooHandler fooHandler, absFoo foo);
}

class FooVisitor implements IFooVisitor{
    public void visit(DefaultFooHandler fHandler, absFoo foo) {
        fHandler.handleFoo(foo);
    }

    public void visit(ImprovedFooHandler iFhandler, absFoo foo) {
        iFhandler.handleFoo(foo);
    }


}

public class Visitor {
    public static void main(String args[]) {
        absFoo df = new Foo();
        absFoo idf = new ImprovedFoo();

        FooHandler handler = new ImprovedFooHandler();

        IFooVisitor visitor = new FooVisitor();
        handler.accept(visitor, idf);

    }
}

But this does not guarantee only Foo can be passed to DefaultFooHandler. 但这不能保证只能将Foo传递给DefaultFooHandler。 It allows ImprovedFoo also can be passed to DefaultFooHandler. 它允许EnhancedFoo也可以传递给DefaultFooHandler。 To overcome, something similar can be done 为了克服,可以做类似的事情

class Foo
{
    protected int z;

}
class ImprovedFoo
{
    protected double k;

}

interface FooHandler {
    void accept(IFooVisitor visitor);
}

class DefaultFooHandler implements FooHandler
{
    private Foo iFoo;

    public DefaultFooHandler(Foo foo) {
        this.iFoo = foo;
    }

    public void accept(IFooVisitor visitor)
    {
        visitor.visit(this);
    }
    public void handleFoo() {
        System.out.println("DefaultFooHandler");
    }
 }

class ImprovedFooHandler implements FooHandler
{
    private ImprovedFoo iFoo;

    public ImprovedFooHandler(ImprovedFoo iFoo) {
        this.iFoo = iFoo;
    }

    public void handleFoo()
    {
        System.out.println("ImprovedFooHandler");
    }

    public void accept(IFooVisitor visitor) {
        visitor.visit(this);
    }

}

interface IFooVisitor {
    public void visit(DefaultFooHandler fooHandler);
    public void visit(ImprovedFooHandler fooHandler);
}

class FooVisitor implements IFooVisitor{
    public void visit(DefaultFooHandler fHandler) {
        fHandler.handleFoo();
    }

    public void visit(ImprovedFooHandler iFhandler) {
        iFhandler.handleFoo();
    }


}
public class Visitor {
    public static void main(String args[]) {
        FooHandler handler = new DefaultFooHandler(new Foo());
        FooHandler handler2 = new ImprovedFooHandler(new ImprovedFoo());

        IFooVisitor visitor = new FooVisitor();
        handler.accept(visitor);

        handler2.accept(visitor);

    }
}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM