【问题标题】:Interface Hell or Acceptable Design?接口地狱还是可接受的设计?
【发布时间】:2012-08-18 07:40:46
【问题描述】:

我正在重构一些遗留代码,这些代码通过一个案例语句一遍又一遍地做着相似的事情:

switch(identifier)
    case firstIdentifier: 
        (SomeCast).SetProperties(Prop1,Prop2,Prop3);
        break;
    ...
    case anotherIdentifier:
        (SomeDifferentCast).SetProperties(Prop1, Prop2, Prop3);
        break;

所以,我尝试创建一个独特的界面,以便它可以成为

(SameInterfaceCast).SetProperties(Prop1,Prop2,Prop3);

但是,我随后发现有些项目甚至没有使用所有属性。于是,我开始想到更像这样的东西:

if(item is InterfaceForProp1)
    (InterfaceForProp1).SetProp1(Prop1);
if(item is InterfaceForProp2)
    (InterfaceForProp2).SetProp2(Prop2);
if(item is InterfaceForProp3)
    (InterfaceForProp3).SetProp3(Prop3);

你可以像这样创建一个类:

public class MyClassUsesProp2And3 : InterfaceForProp2, InterfaceForProp3

但是,我担心这段代码过于分散,它可能会膨胀太多。也许我不应该太害怕本质上是一种方法接口,但我想在走这条路之前看看我是否缺少设计模式? (唯一出现在我脑海中但不太合适的是DecoratorComposite 模式)

更新

所有属性都是唯一类型。

最终,这是依赖注入的一种形式。代码太乱了,现在不能使用像 Ninject 这样的东西,但最终我什至可以摆脱其中的一些并使用注入容器。除了设置变量之外,目前还有一些逻辑正在完成。这都是遗留代码,我只是想一点一点地清理它。

【问题讨论】:

  • Prop1、Prop2 和 Prop3 是同一类型吗?因为我几乎认为您想要一个具有 SetProp() 方法的 InterfaceForProp1、InterfaceForProp2 和 InterfaceForProp3 的超类,该方法接受 Prop1、Prop2 和 Prop3 的任何类型的参数。
  • 也许 Prop1、Prop2 和 Prop3 是尚未由对象表示的域概念的属性。这可能会简化您的界面。
  • @dmn 我刚刚更新了我的问题,并补充说所有类型都不同。如果它们都一样,那么这将是蛋糕。

标签: design-patterns interface strategy-pattern


【解决方案1】:

我不知道对此是否有“正确”的答案,但这就是我会做的。

class Properties {
    prop1
    prop2
    prop3
}

interface PropertySetable {
     setProperties(Properties prop);
}
public class MyClassUsesProp2And3 implements PropertySetable {
    setProperties(Properties prop) {
       //I know I need only 2 and 3
       myProp2 = prop.prop2;
       myProp3 = prop.prop3;
    }

}

在调用函数时,你不应该有演员表。

 someFunc(..., PropertySetable, Properties,...) {
      PropertySetable.setProperties(Properties); 
 } 

这是基本结构。

您应该封装属性 - 将属性设为私有并具有相关的构造函数。或者使用 Builder 模式来构建属性......等等......

【讨论】:

  • 看,但你基本上仍然在这里做同样的事情。我真的很想避免传递甚至没有使用的变量。
  • 不是真的......我更喜欢让类决定他们需要什么......而不是调用代码。您反对传递Properties 对象的原因是什么?对我来说,问班级他们需要什么然后给他们似乎很浪费。拿走所有东西——拿走你需要的东西。由于可维护性,它比传递单个属性更好。如果您要传递指针/引用,那么性能/内存方面也不是问题。
  • +1 因为我想得越多,这种方法就越合适。不过,访问者模式更好。这就是我给它打勾的原因
  • 访客模式更好。但是,它涉及更多工作,您可能需要更多地重构遗留类。但如果你能接受,Visitor 绝对是你要走的路。
【解决方案2】:

我认为答案取决于您首先要重构代码的原因。

  • 如果您想完全摆脱 switch 块,那么您必须使用一种方法实现一个接口,该方法采用三个参数。每个班级都必须弄清楚哪些参数适用以及如何处理这些参数。
  • 如果您想减少代码总量,那么我不确定添加特定于类的属性设置逻辑是否比原始 switch 语句更少代码
  • 如果您想利用编辑器的代码完成功能插入具有正确数量的属性/参数的正确方法,那么只需在每个类上实现正确的方法并完全避免使用接口

【讨论】:

  • IMO,您的第三个选项根本不是理由。我更改代码的主要原因是使代码更易于维护和可读,使其更可靠。访问者模式是最好的方法。
【解决方案3】:

visitor pattern 是您面对 switch 语句和类型转换时的标准解决方案。您的每种情况的代码将进入访问者类中的单独方法。你的每个类将只实现一个接口——一个接受访问者的accept 方法。你最终会得到比现在更多的代码,但它会读起来更清晰。

【讨论】:

    【解决方案4】:

    基本上,您希望使演员(使用 setProp 方法的类型)具有相同的“演员”类型,并使属性(prop1...n)具有相同的“道具”类型。这会将您的代码减少到

    actor.setProp(prop)
    

    如果您想避免使用 instanceOf,我能想到的唯一方法是使用访问者模式,使访问者“道具”。我也会使用模板方法让我的生活更轻松。在 Java 中,我会让它看起来像这样(对于两种实际的 Prop)。

    class Actor {
    
        protected void set(Prop1 p1) {
            // Template method, do nothing
        }
    
        protected void set(Prop2 p2) {
            // Template method, do nothing
        }
    
        public void setProp(Prop p) {
            p.visit(this);
        }
    
        public interface Prop {
            void visit(Actor a);
        }
    
        public static Prop makeComposite(final Prop...props ) {
            return new Prop() {
    
                @Override
                public void visit(final Actor a) {
                    for (final Prop p : props) {
                        p.visit(a);
                    }
                }
            };
        }
    
        public static class Prop1 implements Prop {
            public void visit(Actor a) {
                a.set(this);
            }
        }
    
        public static class Prop2 implements Prop {
            public void visit(Actor a) {
                a.set(this);
            }
        }
    }
    

    这允许你做这样的事情:

        ConcreteActor a = new ConcreteActor();
        Prop p = Actor.makeComposite(new ConcreteProp1(42), new ConcreteProp2(-5));
        a.setProp(p);
    

    ...这真是太棒了!

    【讨论】:

    • 如果我没看错的话,我需要尽可能多地调用我拥有的属性,但它避免了额外的 if's:actor.setProp(Prop1); actor.setProp(Prop2); 等?我知道访问者模式,但从未使用过它,直到现在我对实现非常模糊。如果我的用法是正确的,我真的很喜欢这个......但是我目前无法控制属性类。不过,我将进行更重大的重构,以便以后可以这样做。给你支票,因为你解释得最好,我觉得这是正确的答案(即使我不能使用它)
    • 感谢您的检查!这不是Visitor的常规用法,但它解决了手头的问题。是的,您必须为每个新属性添加一个新的 set(),但如果您添加一个新属性,您只需将其作为新的虚拟模板方法添加到基类 Actor 上,然后为需要能够响应新属性的 Actor 子类。这几乎是最小的变化场景。
    • 我清理了代码并使其更加具体+添加了一种从道具制作复合材料的方法。即使你无法控制实际的属性类,你也可以在重构时将它们包装在这个结构中。
    猜你喜欢
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多