我在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示例能够很好地反映我的问题。
任何让我朝着正确方向前进的想法都会非常有帮助。提前谢谢!
答案 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