Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 关于继承性和可扩展性的一般OO问题_Java_Oop_Design Patterns_Inheritance_Extensibility - Fatal编程技术网

Java 关于继承性和可扩展性的一般OO问题

Java 关于继承性和可扩展性的一般OO问题,java,oop,design-patterns,inheritance,extensibility,Java,Oop,Design Patterns,Inheritance,Extensibility,那么,在单亲继承模型中,在保持相同接口的同时,使代码可扩展以进行未来更改的最佳解决方案是什么(我想强调一个事实,即在最初实施时无法了解这些变化,我的问题的主要焦点是探索支持这些变化的最佳机制/模式,因为它们出现了)?我知道这是一个非常基本的OO问题,下面我将举例说明我是如何解决这个问题的,但我想知道是否有更好的解决方案来解决这个常见问题 下面是我一直在做的事情(示例代码是Java): 开始时创建了以下两个类和接口: public class Foo { protected int z;

那么,在单亲继承模型中,在保持相同接口的同时,使代码可扩展以进行未来更改的最佳解决方案是什么(我想强调一个事实,即在最初实施时无法了解这些变化,我的问题的主要焦点是探索支持这些变化的最佳机制/模式,因为它们出现了)?我知道这是一个非常基本的OO问题,下面我将举例说明我是如何解决这个问题的,但我想知道是否有更好的解决方案来解决这个常见问题

下面是我一直在做的事情(示例代码是Java):

开始时创建了以下两个类和接口:

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
    }
}
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
    }
}
系统仅使用FooHandler类型的变量/字段,并且该对象(在本例中为DefaultFooHandler)是在一些定义良好的地方创建的(可能有一个FooHandlerFactory),以便补偿将来可能发生的任何更改

然后,在将来的某个时候,需要扩展Foo来添加一些功能。因此,创建了两个新类:

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
    }
}
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
    }
}
在上面的例子中,让我畏缩的是出现在
ImprovedFoodHandler.handleFoo


有没有办法避免使用
if语句和
操作符的
实例?

是的,不要违反LSP,这就是你在这里所做的。你考虑过策略模式吗?

在这种情况下,我通常使用工厂为我拥有的Foo类型获取合适的FooHandler。在这种情况下s case仍然会有一组ifs,但它们将在工厂中,而不是在处理程序的实现中。

这看起来像是基本多态性的简单例子。给Foo一个名为DontWorryI'llhandlethismif()的方法(嗯,除了没有撇号,还有一个更合理的名称).FooHandler只调用给定的任何Foo的此方法。Foo的派生类可以随心所欲地重写此方法。问题中的示例似乎有很多内容。

首先,您编写的所有代码都不起作用。 每次看到
instanceof
if…else
时都要非常小心。这些检查的顺序非常重要。在你的情况下,你永远不会执行
handleImpovedFoo
。猜猜为什么:)

你有这些
语句的
实例是绝对正常的。有时,这是为子类型提供不同行为的唯一方法。
但在这里你可以使用另一个技巧:使用simple
Map
。将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 );

Map处理此问题的最佳方法过于依赖于具体情况,无法提供通用解决方案。所以我将提供一些例子,以及如何解决它们

案例1:虚拟文件系统

代码的客户端实现了虚拟文件系统,使它们能够操作任何类型的资源,这些资源看起来像文件。它们通过实现以下接口来实现

interface IFolder
{
     IFolder subFolder(String Name);
     void delete(String filename);
     void removeFolder(); // must be empty
     IFile openFile(String Name);
     List<String> getFiles();
}
每当客户端注册IFolder(而不是IFolder2)时,注册

new IFolder2Adapter(folder)
而是在整个应用程序中使用IFolder2。您的大多数代码都不应该关心旧版本IFolder支持哪些方面的差异

案例2:更好的字符串

您有一个支持各种功能的string类

class String
{
     String substring(int start, end);
}
您决定在新版本中添加字符串搜索,从而实现:

class SearchableString extends String
{
    int find(String);
}
这太傻了,SearchableString应该合并到String中

案例3:形状

您有一个形状模拟,它可以让您获得形状的面积

class Shape
{
    double Area();
    static List<Shape> allShapes; // forgive evil staticness
}
我们可以为Shape添加默认的空绘制方法。但是让形状有一个绘制方法似乎是不正确的,因为形状通常不打算被绘制。绘图确实需要可绘制形状的列表,而不是提供的形状列表。事实上,可能DrawableShape根本不应该是一个形状

案例4:零件

假设我们有一辆车:

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

void maintain(Car car)
{
    car.getMotor().changeOil();
    car.getWheels().rotate();
}
当然,你知道在未来的某个地方,有人会制造出更好的汽车

class BetterCar extends Car
{
    Highbeams getHighBeams();
}
在这里,我们可以使用访问者模式

void maintain(Car car)
{
     car.visit( new Maintainer() );
}
car将其所有组件传递给调用到ICarVisitor接口,允许Maintainer类维护每个组件

案例5:游戏对象 我们有一个可以在屏幕上看到各种物体的游戏

class GameObject
{
   void Draw(Painter painter);
   void Destroy();
   void Move(Point point);
}
我们的一些游戏对象需要能够定期执行逻辑,因此我们创建:

class LogicGameObject extends GameObject
{
    void Logic();
}
我们如何在所有LogicGameObjects上调用Logic()?在这种情况下,向GameObject添加一个空的Logic()方法似乎是最好的选择。它完全在游戏对象的工作描述中,期望它能够知道如何进行逻辑更新,即使它什么都没有

结论


处理这种情况的最佳方法取决于个人情况。这就是为什么我提出了一个问题,为什么您不想将功能添加到Foo。扩展Foo的最佳方式取决于您到底在做什么。如果显示instanceof/if,您会看到什么,这表明您没有以最佳方式扩展对象

使用访问者模式,您可以执行类似的操作

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);

    }
}
但这并不保证只有Foo可以传递给defaultfoodhandler。它允许将ImprovedFoo传递给DefaultFoodHandler。为了克服这个问题,可以做类似的事情

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);

    }
}

你在寻找访客模式吗@Erik,你应该把它作为答案发布)@Stas:那么我必须总结一下模式-其他人来做:)@Manos
ImprovedFoo
extends
Foo
。第一个问题应该是你为什么不想将功能添加到Foo中。