Java 设计决策:扩展接口与新接口

Java 设计决策:扩展接口与新接口,java,Java,你好 今天我有一个小小的设计决定:有一个名为“TargetSystem”的现有接口,它只有一个方法“getName()”。关于这些目标系统没有其他通用信息。 现在我有了一种新的需要身份验证的目标系统 我必须知道目标系统是否需要身份验证(前端必须显示这些系统的密码对话框)。如果需要身份验证,我必须设置用户名和密码 我的设计决策:我应该使用“needsAuthentication”和“setUsernameAndPassword”方法扩展现有接口,还是创建一个新接口,仅使用“setUsernameA

你好

今天我有一个小小的设计决定:有一个名为“TargetSystem”的现有接口,它只有一个方法“getName()”。关于这些目标系统没有其他通用信息。 现在我有了一种新的需要身份验证的目标系统

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

我的设计决策:我应该使用“needsAuthentication”和“setUsernameAndPassword”方法扩展现有接口,还是创建一个新接口,仅使用“setUsernameAndPassword”方法扩展旧接口,通过instanceof获得认证需求


重要提示:无需向下兼容或任何其他原因不接触旧界面!我刚和一位同事讨论过,通常哪种方法比较好:创建名为“ObjectWithFeatureX”、“ObjectWithFeatureY”的接口,或者创建名为“hasFeatureX”、“hasFeatureY”的方法。

一般来说,如果设计好,不需要实例

IMHO:InstanceCof只能用于不能更改的类/接口

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

创建名为“ObjectWithFeatureX”、“ObjectWithFeatureY”的接口或创建名为“hasFeatureX”、“hasFeatureY”的方法

我认为两者都不是在可能的情况下,调用者不应该有这样的代码

if(a instanceof NeedsUsername) {
    ((NeedsUsername) a).setUsername(username);
}

它应该有

a.setUsername(username);
编辑:您需要某种类型的侦听器来处理诸如密码失败之类的事件。你可以有一个这样的倾听者

public interface AuthenticationListener {
   public void firstUsernamePassword();
   public void failedAuthentication(String reason);
}

自己问一个问题:
AuthenticationSystem
is-a
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
   }
   ...
}
通常哪种方法比较好:创建名为“ObjectWithFeatureX”、“ObjectWithFeatureY”的接口,或者创建名为“hasFeatureX”、“hasFeatureY”的方法

你可能会问自己的另一个问题是关于未来的计划。你打算拥有更多的功能吗?如果你能看到有一天拥有ObjectWithFeatureXAndFeatureY的可能性,你可能会考虑装饰设计师的设计模式。

这个向windows添加多个功能(如滚动条)的示例展示了装饰器模式的良好使用


如果你确信你永远不需要这么多功能,只需要使用简单的继承,那就要小心不要设计过度。

我不太同意Peter的观点。有时,instanceof甚至可以在设计中扮演中心角色

就个人而言,我喜欢以下图案(防火服:“on”):

然后,在代码中:

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,另一个不具有功能X,以便对其进行不同的处理

在这些特性上运行的系统部分只需通过检查instanceof来检查对象是否具有属性A、B或C。这是可扩展的、向后兼容的和简单的


也就是说,这是一种非常特殊的处理方式,不一定适合一般用途的东西。如果您有许多正交特性应用于多个不同的对象,那么它尤其适合。

您总是可以在不进行检查的情况下重构到系统,但这通常与良好的分层方法相冲突

例如:在您的情况下,如果只想在目标系统需要时显示登录对话框,可以使用接口方法
init()
,它在一种情况下显示身份验证对话框,而在另一种情况下不执行任何操作。但是,您在目标系统中混合了gui代码,这不是您想要的。你可以开始用回调之类的方式在Udn上争论,但最终没有简单的解决方法

所以这里有一个我喜欢的方法

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方法。不过,您可能需要想出更好的名字:)


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

对用户名和密码使用一个或两个方法不是重点,事实上我使用setPasswordAuthentication(PasswordAuthentication)。但“instanceof”和“needAuthentication”方法看起来都很丑陋。我不能仅仅调用该方法,因为前端必须知道是否显示密码对话框-这取决于目标系统。在这种情况下,我建议您需要进行必要的身份验证。另一种方法是使身份验证失败(如果未设置任何设置),从而触发对话框。(你需要以某种方式反馈密码需要再次尝试,你可以使用此侦听器)我也已经想到了一种检查登录是否正确的方法。事实上,我可以在第一次触发对话框之前调用此方法-如果系统不需要身份验证,则可以在不使用任何用户名/密码的情况下返回true。谢谢corse,但这正是我问题的目标。应该是作者吗
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(...)
    ...
  }
}
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);
   }

...