如果我的问题真的很琐碎的话,很抱歉。但是我觉得我不擅长‘设计’,所以想要对这个话题有一些见解。
假设我有一个空接口果树,有不同的java实现,比如Apple、Mango、Orange等等。
我有一台FruitBasket,里面有各种水果的开胃机。
basket.setApple(Apple a);
basket.setOrange(Orange o);诸若此类。
现在,当我需要为篮子设置一个特定的水果时,我需要调用正确的设置器。其中一种方法是拥有一个Utility,它将遍历所有te模型对象(成果),并调用篮子上相应的setter。
就像这样。
public class FruitUtilty
{
public static void setAllFruits(FruitBasket basket, List<Fruits> fruits)
{
for(Fruit fruit : fruits)
{
if(fruit.getClass() == Apple.class)
{
basket.setApple((Apple)fruit);
}
else if(fruit.getClass() == Mango.class)
{
basket.setMango((Mango)fruit);
}
else if(fruit.getClass() == Orange.class)
{
basket.setOrange((Orange)fruit);
}
}
}
}但我在这里看到的问题是丑陋的如果/否则的梯子。每次添加模型对象时,都必须修改该实用程序。
因此,我提出了一个设计,其中接口不会是空的,而是对所有要实现的结果具有“setBasket”行为。即
public interface Fruit
{
public void setBasket(FruitBasket b);
}现在,所有的成果(应该是哑模型对象)都在这个way.For中实现了这个接口。苹果会是这样的
public class Apple implements Fruit {
....
....
....
public void setBasket(FruitBasket b) {
b.setApple(this);
}
}现在,我们不再需要FruitUtility类了,无论在什么地方调用了结果实用程序的setAllFruits,我们都可以像这样使用代码片段
FruitBasket f = new FruitBasket();
List<Fruit> fruits = getAllFruits();
for(Fruit fruit: fruits)
{
fruit.setBasket(f);
}
........
........我的团队负责人说,这是一个非常糟糕的设计,因为我已经“稀释”了模型对象。我们的模型应该是一个“哑巴”对象,只能携带数据。因此,我们应该坚持以实用程序为基础的方法,或者除了这个方法以外的任何其他方法。
做事情的正确/最好的方法是什么(至少在JAVA中)?
提前谢谢。
发布于 2014-02-24 12:09:19
我认为在这种情况下水果应该是“哑巴”。今天你把它们加到篮子里,明天你会在商店里把它们卖出去,一周后你会把它们混合成沙拉:)
如果将一个'setBasket‘方法添加到水果接口中,那么这两个概念就会在接口级别上耦合起来,这是不好的。另一方面,如果不是可扩展的,正如您已经说明过的那样。
我认为最好的方法(至少这就是我解决这个问题的方法)是引入另一个抽象,比如" action“,它表示可以对结果执行的操作。
如果我们谈论的是篮子,我认为您可以创建一个公共接口,并为每种水果实现它。
示例:
public interface FruitBasketAdder {
void addFruit(Fruit fruit, Basket basket);
}
public class AppleBasketAdder implements FruitBasketAdder {
public void addFruit(Fruit fruit, Basket basket) {
Apple apple = (Apple) fruit;
basket.addApple(apple);
....
}
}
public class OrangeBasketAdder implements FruitBasketAdder {
public void addFruit(Fruit fruit, Basket) {
Orange orange = (Orange) fruit;
basket.addOrange(orange);
....
}
}现在,您可以生成一个注册表,它表示可以处理各种水果的策略。如下所示:
Map<Class<Fruit>>, FruitBasketAdder> registry = new HashMap<>();
registry.put(Orange.class, new OrangeBasketAdder());
registry.put(Apple.class, new AppleBasketAdder());
.....最后一点是使用这个注册表。比方说,您有一个常用的方法,它接受一个“水果”列表作为参数,并尝试将其添加到篮子中,就像您在示例中用"if“表示的那样。因此,您应该从注册表中找到指定的" BasketAdder“(假设您可以访问对注册表的引用),并通过调用相关的BasketAdder将水果添加到篮子中。给你:
public void addFruitsToTheBasket(List<Fruit> fruits, Basket basket) {
for(Fruit fruit : fruits) {
BasketAdder adder = registry.get(fruit.getClass());
adder.addFruit(fruit, basket);
}
}这个设计比"if“要好得多,因为它允许在不破坏接口的情况下动态添加新的水果。只需创建一个BasketAdder,将其传递到注册表,您就完成了,主要算法(遍历+添加到篮)保持不变。
标记
希望这能有所帮助
发布于 2014-02-24 11:49:13
我认为你的团队领导提出了一个正确的观点。您正在将功能从FruitBasket转移到Fruit,但是Fruit甚至不应该了解FruitBasket。如果您有一个FruitBag和一个FruitBox,会发生什么呢?无论何时引入这样的类,您都必须更改每个Fruit实现者。
使用正确的setter函数的逻辑是FruitBasked内部的,应该保持不变。
关于“最好的方法”。在这里我没有想到最好的解决办法。水果种类之间的区别必须在某个地方进行。if .. else可能不是最漂亮的解决方案,但看起来不错。不过,使用enum和switch/case可能更好。这样,当您忘记一种类型时,就会得到编译器的警告。
通常情况下,整个setApple与setOrange方法是问题的根源。这样就不需要苹果和橘子来实现Fruit了。但是,要想摆脱这种局面,通常是不容易的。它总是取决于细节。
发布于 2014-02-24 11:47:04
这取决于周围的工具链和/或开发风格,如果您有一个愚蠢的数据结构“模型”或一个实现业务逻辑--在这两个极端之间有许多不同之处,那么这个问题通常是无法回答的。但是实用程序类方法有一个主要的缺点,那就是关注点的分离,因为查看水果篮类,没有人知道水果分离是如何实现的,而不检查哪些类访问水果篮,这样在最坏的情况下,有人在其他地方实现相同的算法,因为他不知道这一个实用程序类。
https://stackoverflow.com/questions/21986694
复制相似问题