Java 关于继承性和可扩展性的一般OO问题
那么,在单亲继承模型中,在保持相同接口的同时,使代码可扩展以进行未来更改的最佳解决方案是什么(我想强调一个事实,即在最初实施时无法了解这些变化,我的问题的主要焦点是探索支持这些变化的最佳机制/模式,因为它们出现了)?我知道这是一个非常基本的OO问题,下面我将举例说明我是如何解决这个问题的,但我想知道是否有更好的解决方案来解决这个常见问题 下面是我一直在做的事情(示例代码是Java): 开始时创建了以下两个类和接口:Java 关于继承性和可扩展性的一般OO问题,java,oop,design-patterns,inheritance,extensibility,Java,Oop,Design Patterns,Inheritance,Extensibility,那么,在单亲继承模型中,在保持相同接口的同时,使代码可扩展以进行未来更改的最佳解决方案是什么(我想强调一个事实,即在最初实施时无法了解这些变化,我的问题的主要焦点是探索支持这些变化的最佳机制/模式,因为它们出现了)?我知道这是一个非常基本的OO问题,下面我将举例说明我是如何解决这个问题的,但我想知道是否有更好的解决方案来解决这个常见问题 下面是我一直在做的事情(示例代码是Java): 开始时创建了以下两个类和接口: public class Foo { protected int z;
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
。猜猜为什么:)
你有这些语句的实例是绝对正常的。有时,这是为子类型提供不同行为的唯一方法。
但在这里你可以使用另一个技巧:使用simpleMap
。将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:那么我必须总结一下模式-其他人来做:)@ManosImprovedFoo
extendsFoo
。第一个问题应该是你为什么不想将功能添加到Foo中。