设计具有通用接口但行为不同的类

时间:2012-03-13 17:27:29

标签: java oop scala design-patterns

我在Java和Scala上都标记了这个问题,因为虽然我主要使用Java开发,但我想看看Scala上的解决方案是否会有所不同。

我在为我的应用程序设计类时遇到问题。我有一组具有不同行为的常见对象。正如我最近阅读了一些关于模式的书,我告诉他:“好的,我可以在这里使用策略模式”,将行为定义为一些字段对象并将其委托给所有逻辑。以下是我的问题:)

假设我有一个可以飞行的基础鸭子,我将飞行委托给一些FlyBehaviour。

interface IFlyable { void fly(); }

interface IFlyBehaviour { void fly(); }

class Duck implements IFlyable {
    IFlyBehaviour flyBehaviour;

    void fly() {
        flyBehaviour.fly();
    }
}

但我的鸭子有点不同,我意识到我希望这种行为依赖于它。首先,我有一个SpaceDuck应该在太空中飞行,并使用仅为SpaceDuck定义的spaceShip字段。接下来我有一个HelicopterDuck,我想尽可能低地飞行,并使用一些仅为HelicopterDuck定义的防空火炬。所以在代码中它就是这样的

class SpaceDuck extends Duck {
    String spaceship;
}

class SpaceFlyBehaviour implements IFlyBehaviour {
    void fly() {
        System.out.println("Flying in space on spaceship: " + spaceduck.spaceship);
    }
}

class HelicopterDuck extends Duck {
    int flares;
}

class HelicopterFlyBehaviour implements IFlyBehaviour {
    void fly() {
        while(helicopterduck.flares > 0) {
            System.out.println("I'm going low and using flares");
            helicopterduck.flares--;
        }
    }
}

在我的行为实现中,我实际上没有对spaceduck或helicopterduck的引用,并且此代码将无法编译。我刚刚提供了一个我想象的样本,并希望它是如此。我可以修改IFlyBehaviour并将duck作为参数传递给fly()方法但是我必须向下转换才能访问鸭子特定的字段,这不是我想的。

看起来很明显的方法就是将IFlyBehaviour丢弃并将逻辑移动到每只鸭子的fly()方法。但是我期待很多不同的空间飞行行为和直升机行为,而fly()并不是唯一的方法。它将是squack(),run()等,每个都有不同的行为。所以我的班级层次结构将变得庞大且不受支持。

在我的实际应用程序中,我将有一些可运行和可停止的实例,可以以不同的方式运行和停止。一个实例将通过SSH脚本启动,另一个实例通过MBean(或SSH,它取决于用户如何配置它),第三个使用第三方等等。所以我希望Duck示例能够很好地反映我的问题。

任何让我朝着正确方向前进的想法都会非常有帮助。提前谢谢!

3 个答案:

答案 0 :(得分:2)

在Scala中,traits仅包含此类内容。

在Java中,它更难。我会尝试摆脱duck子类而不是行为,将特定于类型的属性移动到行为类中。如果你只通过它们的公共接口使用ducks,那么这些属性无论如何都是不可用的 - 它们只能在实例化时看到。

答案 1 :(得分:2)

首先,您尚未初始化flyBehaviour课程中的Duck。你可以像这样初始化它:

class SpaceDuck extends Duck {
    String spaceship;
    public Duck() {
        setFlyBehavior(new SpaceFlyBehaviour(this));
    }
}

或者,您可以在行为的方法中提供鸭子的参考:

interface IFlyable { void fly(); }

interface IFlyBehaviour { void fly(IFlyable flyable); }

或者最简单的方法:

public abstract class Duck {
    public abstract void fly();
}

public class SpaceDuck extends Duck {
    String spaceship;
    public void fly() {
        System.out.println("Flying in space on spaceship: " + spaceduck.spaceship);
    }
}

答案 2 :(得分:1)

在scala中你可以这样做:

abstract class Duck {def fly: Unit}

class BasicDuck extends Duck { def fly {println("flying")} }

trait SpaceshipFlight extends Duck {
    def spaceship: String
    abstract override def fly() {
        super.fly
        println("but now I'm in space on a: " + spaceship);
    }
}

trait Flares extends SpaceshipFlight {
    var flares: Int
    abstract override def fly() {
        super.fly
        while(flares > 0) {
            println("I'm going low: flares are at " + flares)
            flares=flares-1
        }
    }
}

在呼叫站点,您可以将您喜欢的特征与鸭子混合

scala> new BasicDuck with SpaceshipFlight {def spaceship="rocket"}
res1: BasicDuck with SpaceshipFlight = $anon$1@751d3ec8

scala> res1.fly
flying
but now I'm in space on a: rocket

//now with flares!
scala> new BasicDuck with SpaceshipFlight with Flares {def spaceship="rocket"; var flares=5}
res2: BasicDuck with SpaceshipFlight with Flares = $anon$1@4f6b3939

scala> res2.fly
flying
but now I'm in space on a: rocket
I'm going low: flares are at 5
I'm going low: flares are at 4
I'm going low: flares are at 3
I'm going low: flares are at 2
I'm going low: flares are at 1

请注意,我稍微更改了您的示例以显示在scala中您还可以使一个特征覆盖前一个特征的行为,因此您可以使用“超级”,就像我们在SpaceshipFlight和Flares特征中所做的那样traits本身“可堆叠”(这实际上称为“stackable trait pattern”)

编辑添加第二种方式(在实例化后可以注入不同的行为)

class Duck
def fly[D <: Duck](duck:D, flyBehavior: (D => Unit)) {
    flyBehavior(duck)
}      

class SpaceDuck(val spaceship: String) extends Duck 

val d = new SpaceDuck("rocket") 

// a behavior is now simply a function (no need to wrap it in a class)
val spacefly = (d: SpaceDuck) => println("flying on a " + d.spaceship)
val normalfly = (d: SpaceDuck) => println("flying normally. Not using my " + d.spaceship)

// using different behaviors at runtime
fly(d, spacefly) // flying on a rocket
fly(d, normalfly) // flying normally. Not using my rocket