假设我有一个类Foo
,其中有一堆逻辑,另一个类Bar
基本相同。但是,由于Foo
和Bar
是不同的(但相关的)实体,我需要从我的代码中明显区别(即我可以判断实例是Foo
还是{{{ 1}})
当我在没有太多考虑的情况下一起打击这个时,我最终得到了以下内容:
Bar
这样好吗?使public class Foo {
/* constructors, fields, method, logic and what-not */
}
public class Bar extends Foo {
/* nothing here but constructors */
}
成为复合类更好吗? e.g:
Bar
甚至,当我们接受它时,更低技术的东西:
public class Bar {
private Foo foo;
/* constructors and a bunch of wrapper methods that call
into foo */
}
你怎么看? 您如何处理这些'标记类'?
作为我的代码可能希望以不同方式处理public class Foo {
/* constructors, fields, method, logic and what-not */
private boolean isABar; // Could be an enum
}
和Foo
的示例,我的代码需要能够执行Bar
和List<Foo>
之类的操作。 List<Bar>
无法进入Foo
,反之亦然。
答案 0 :(得分:14)
在我看来,如果Foo
和Bar
是一个共同的祖先类(也许是AbstractFoo
)的子类,它具有所有功能,这是最好的。 Foo
和Bar
之间应该存在哪些行为差异?将差异编码为AbstractFoo
中的抽象方法,而不是在代码中使用if
语句。
示例:而不是:
if (foo instanceof Bar) {
// Do Bar-specific things
}
请改为:
class Bar extends AbstractFoo {
public void specialOp() {
// Do Bar-specific things
}
}
// ...
foo.specialOp();
这种方法的好处是,如果你需要一个第三类,那就像Foo
,但只有一点点不同,你不需要经历所有代码并添加编辑所有if
陈述。 : - )
答案 1 :(得分:3)
这完全取决于Foo和Bar类的含义。他们代表什么,他们的目的是什么。请澄清。
我可以想象您的每个解决方案和建议的解决方案都是正确的情况。
答案 2 :(得分:2)
我会创建一个类FooBarImplementation来实现Foo和Bar的常用功能。 Foo和Bar将从中衍生出来。但是在您的代码中,永远永远不会使用FooBarImplementation类型。我的Java时代有点落后于我,但我想必须有某种方法可以隐藏用户代码中的FooBarImplementation(使其受到保护,或者仅包装可见,具体取决于您的项目组织。这样,没有用户代码会混合Foo对于一个酒吧(反之亦然)?
class FooBarImplementation
{
public void doSomething() { /* etc. */ }
/* etc. */
}
class Foo inherits FooBarImplementation { /* etc. */ }
class Bar inherits FooBarImplementation { /* etc. */ }
另一种可能性是让Foo和Bar将他们的每个方法转发到内部类(同样,FooBarImplementation)。这样,用户代码就无法成为Foo和Bar。
class FooBarImplementation
{
public void doSomething() { /* etc. */ }
/* etc. */
}
class Foo
{
private FooBarImplementation fooBarImplementation = new FooBarImplementation() ;
public void doSomething() { this.fooBarImplementation.doSomething() ; }
/* etc. */
}
class Bar
{
private FooBarImplementation fooBarImplementation = new FooBarImplementation() ;
public void doSomething() { this.fooBarImplementation.doSomething() ; }
/* etc. */
}
Shoudl Foo继承自Barn,就语言而言,Foo将是一个Bar。不要这样做,你将失去对象之间的差异,这就是你不想要的。
这是你可能遇到的最糟糕的想法。 Bjarne Stroustrup警告不要使用C ++的这种反模式,而C ++并不是关于OOP的。所以我猜这种模式对于Java来说更加“反”......: - )
答案 3 :(得分:1)
如果Foo
和Bar
有可能在某一天的实施中出现分歧,那么您的问题就会得到解答 - 以最好的方式使用继承。
但如果你绝对肯定他们永远不会分歧,那么显然你正在寻找应该由一个类来代表的东西,例如ThingThatIsEitherFooOrBar
。
使用该类创建,而不是给它一个像isFoo
这样的布尔属性,再看看为什么需要将Foo与Bar区分开来会好得多。什么是Foos让你处理它们不同于酒吧?想出来,并创建一个指定不同信息的属性。 Foos更大吗?然后创建一个大小的属性(即使它是一个值为“Foo-sized”和“Bar-sized”的枚举)。
如果没有具体的Foo和Bar可能的例子,这就差不多了。
答案 4 :(得分:1)
基本上您需要应用Strategy Pattern。
答案 5 :(得分:0)
绝对使用布尔属性。这是最简单的解决方案,除非您预见到Bar类需要稍后更改它的界面(例如覆盖它的方法)。
答案 6 :(得分:0)
继承是最好的 使用布尔属性,类必须知道两种不同类型的对象的存在,并且这不容易扩展到两个以上。而且,这种方法不会让你超载功能 组合使您可以为所有函数编写包装器。
答案 7 :(得分:0)
你的数据不够清晰,但基于我认为你需要的东西,我很困惑你为什么不简单地选择第四种选择:
Class MainStuff;
Class TypeA;
Class TypeB;
要么使TypeA和B继承MainStuff,要么使MainStuff成为TypeA和TypeB的数据成员。这取决于这3个类的含义。
答案 8 :(得分:0)
正如其他人所说,这取决于,但如果你在Foo和Bar之间有共同的功能,并且功能上的差异可以表示为参数,那么我投票给子类。 这基本上就是我们在实际软件课程结束时所做的工作。
我们应该实现一个数独游戏,最后,我们有一个“AbstractPlayfield”,它能够将任意规则集应用于任意游戏场。这个AbstractPlayfield由我们应该实现的各个变体子类化。这些子类为抽象游戏场设置参数(主要是棋盘的规则和形状),一切都像魅力一样。我们甚至在这些子类中得到了更多的继承,因为其中一些变体包含规则“数字必须在一行中唯一”和“数字在列中必须唯一”。
使用它,我们能够在大约3天内完成估计大约2个月的工作:)(他们用“测试那些微小的属性设置类来惹恼我们,因为你可能有错误!测试它们!测试他们!我们不在乎所有重要的逻辑都经过测试!“。)
另一方面,如果类Bar没有Bar的特殊功能,我没有看到添加它的重点 - 至少从你给我的数据。如果你想根据类型和类型调度做一些操作可能有意义,但我无法从Foo和Bar中读到它。在这种情况下,由于YAGNI,我不会创建Bar。
答案 9 :(得分:0)
如果Foo和Bar之间没有行为差异,那么类名“Foo”就不够抽象。确定Foo和Bar之间的通用抽象,并相应地重命名该类。然后在类中提供一个成员字段,将实例标识为“Foo”,“Bar”等。如果您希望将可能的值限制为“Foo”和“Bar”,请使用枚举。