设计决策:扩展界面与新界面

时间:2011-02-08 09:04:42

标签: java

喂,

我今天做了一个小设计决定:有一个名为'TargetSystem'的现有界面,只有一个方法'getName()'。没有关于这些目标系统的其他常见信息。 现在我有了一种需要身份验证的新型目标系统。

我必须知道目标系统是否需要身份验证(前端必须显示密码对话框)。如果需要身份验证,我必须设置用户名和密码。

我的设计决定:我是否应该使用方法'needsAuthentication'和'setUsernameAndPassword'扩展现有接口,或者创建一个仅使用方法'setUsernameAndPassword'扩展旧接口的新接口,通过instanceof获取认证需求。

重要提示:无需向下兼容或任何其他不接触旧界面的原因!我只是和同事讨论,这种方式通常很好:创建具有'ObjectWithFeatureX','ObjectWithFeatureY'等名称的接口或创建'hasFeatureX','hasFeatureY'等方法。

5 个答案:

答案 0 :(得分:2)

自己提问:AuthenticationSystem TargetSystem

没有垂头丧气的解决方案:

interface TargetSystem{
   //Each TargetSystem needs a sort of authentication anyway
   boolean authentication(AuthenticationContext context);
   ...
}

class NormalTargetSystem implements TargetSystem{
   boolean authentication(AuthenticationContext context){
       //dummy authentication
       return true;
   }
   ...
}
class AuthenticationTargetSystem implements TargetSystem{
   boolean authentication(AuthenticationContext context){
       //real authentication
   }
   ...
}

答案 1 :(得分:2)

我真的不同意彼得。有时,实例甚至可以在设计中发挥核心作用。

Personnaly,我喜欢以下图案(防火服:“上”):

interface Authentifiable {
    void authentify(...)
}

interface Stateful {
    void saveState(...)
    void loadState(...)
}

interface MyOtherAspect {
   ...
}

然后,在代码中:

void someCode()
{
  for (Server s : servers)
  {
    if (s instanceof Authentifiable)
       ((Authentifiable) s).authentify(...)
    if (s instanceof Stateful)
       ((Stateful) s).load(...)
    ...
  }

  for (GridSystem gs : grids)
  {
    if (gs instanceof Authentifiable)
       ((Authentifiable) gs).authentify(...)
    if (gs instanceof Stateful)
       ((Stateful) gs).load(...)
    ...
  }
}

这使您可以在任何对象上使用完全正交的“方面”。您可以拥有对象实现功能A& B,其他B& C和其他人A& C. ...或任何功能的任何组合。 如果你有很多这样的功能,这个特别方便。为所有实现对象的人创建一个大的接口只是处理所有这些带有空存根的功能可能很难看。

另外,在这里,您可以检查特定对象是否具有可以直接使用的特定特征,例如将对象列表拆分为两个束,一个具有特征X而另一个没有,以便处理他们不同。

在这些功能上运行的系统部分只需要通过检查instanceof来检查对象是否具有属性A,B或C.这是可扩展的,向后兼容且容易的。

也就是说,这是处理事物的一种非常具体的方式,并不一定适合通用的东西。如果你有很多正交特征应用于几个不同的对象,它尤其适用。

答案 2 :(得分:1)

一般来说,如果你有一个好的设计,你就不需要instanceof。

恕我直言:instanecof只能用于你无法改变的类/接口。

你可以只使用setUsernameAndPassword()而不需要它的实现只是忽略它吗?更常见的方法是使用setUsername()和setPassword()(但是我更喜欢一体化方法,因为改变一个方法没有多大意义)

  

使用'ObjectWithFeatureX','ObjectWithFeatureY'等名称创建界面或创建'hasFeatureX','hasFeatureY'等方法。

我不会说。 ;)尽可能调用者不应该有像

这样的代码
if(a instanceof NeedsUsername) {
    ((NeedsUsername) a).setUsername(username);
}

if(a.needUsername()) {
    a.setUsername(username);
}

它应该只有

a.setUsername(username);

编辑:您需要某种类型的侦听器来处理密码失败等事件。你可以有一个像

这样的听众
public interface AuthenticationListener {
   public void firstUsernamePassword();
   public void failedAuthentication(String reason);
}

答案 3 :(得分:0)

  

哪种方式通常很好:创建具有'ObjectWithFeatureX','ObjectWithFeatureY'等名称的接口或创建'hasFeatureX','hasFeatureY'等方法。

您可能会问自己的另一个问题是未来的计划。你打算有更多功能吗?如果您可以看到有一天有ObjectWithFeatureXAndFeatureY的可能性,您可能希望考虑Decorator设计模式。

向窗口添加滚动条等多个功能的示例显示了装饰器模式的良好用法。 http://en.wikipedia.org/wiki/Decorator_pattern#Motivation

如果您确定永远不需要这么多功能并且可以使用简单的继承,请注意不要过度设计。

答案 4 :(得分:0)

您总是可以重构到没有检查的系统,但这通常会与一个很好的分层方法冲突。

例如:在您的情况下,如果您只想在目标系统需要时显示登录对话框,您可以使用一个接口方法init(),在一种情况下显示身份验证对话框,在另一种情况下不执行任何操作。但是你在你的目标系统中混合了gui代码,这不是你想要的。你可以用回调和所有这些来开始麻烦aroudn,但最后,没有简单的方法。

所以这是我喜欢的一种方法

public interface Authenticating {
  void authenticate(String username, String password);
}

public interface TargetSystem {
  String getName();

  /**
  * @return the authentication interface of this object, or 
  * null if authentication is not required.
  */
  Authenticating getAuthenticationInterface();
}

...

   Authenticating auth = targetSystem.getAuthenticationInterface();
   if (auth!=null) {
     String user = null;
     String pass = null;
     // show login dialog and get response
     auth.authenticate(user, pass);
   }

...

这样,只有在需要时才能调用authenticate方法。你可能需要考虑更好的名字:)

在这种情况下,我不会让一个接口扩展另一个接口。目标系统可以实现两个接口并只返回自身,或者它可以返回一个匿名内部类。但这完全取决于它的实现者。