【问题标题】:Name of a design pattern involving cross-casting涉及交叉转换的设计模式的名称
【发布时间】:2013-12-11 16:58:29
【问题描述】:

在使用 Selenium WebDriver 的过程中,我注意到他们的 API 中有一种模式以及他们的类层次结构。

api是这样设置的:

Interface A {
   void doAStuff();
}
Interface B {
   void doBStuff();
}
Interface C {
   void doCStuff();
}
Class X implements A, B, C {
   ...
}
Class Y implements A, B, C {
   ...
}
Class Z implements A, B, C {
   ...
}

实现如下所示:

public void doStuffWithImplementationsOfA(A a) {
   a.doAStuff();
   B b = (B) a;
   b.doBStuff();
   C c = (C) a;
   c.doCStuff();
}

public static void main(String[] args) {
   A a = new X();
   doStuffWithImplementationsOfA(a);
}

如您所见,接口 A、B 和 C 根本不相互扩展,但在 API 中,A 的所有实现也实现了 B 和 C,因此允许交叉转换。具体来说,在 Selenium 中,WebDriver 的所有实现(如 FirefoxDriver 和 ChromeDriver)还实现了其他几个常见的不相关接口(HasInputDevices、JavascriptExecutor 等);同样,WebElement 的所有实现都实现了其他常见的不相关接口(如可定位)。这不仅允许,而且需要在接口之间进行交叉转换以访问每个实现中的各种方法。

我的问题,分为三个部分:

  1. 这种层次结构是否符合设计模式?
  2. 如果有,它有名字吗?
  3. 以这种方式设计 API 有哪些优点/缺点(而不是仅仅让 C 扩展 B,B 扩展 A)?

编辑:这是 WebDriver API 层次结构的图表(并非详尽无遗)。

【问题讨论】:

  • 它可能有一个名字,但假设A BC 不相互依赖,不要强制它。输入每个接口名称的额外字符对于它提供的简洁代码是一个很好的权衡。 (仅仅因为我有实现IterableSerializable 的东西并不意味着我应该做一个扩展两者的东西)
  • 这看起来很可怕。我希望我永远不必这样做……永远。
  • @MrTi “额外的字符......对于干净的代码来说是一个很好的权衡”:这似乎是这样设计 API 的一个很好的理由;它在 API 中提供了凝聚力。交叉铸造本身似乎是一个缺点,所以我想知道这种设计是否还有其他好处,或者在这种情况下是否可以阐述内聚的好处?
  • 如果我只想扩展其中的 1 或 2 个,而不是全部三个。例如,不强制驱动程序允许截取屏幕截图。 (他们可以发布一个不截屏的驱动程序,并在未来实现一个待办事项)
  • @MrTi 很好的观察。我看到 RemoteWebDriver 和 HTMLUnitWebDriver 没有实现 TakesScreenshot。我在我的问题中附上了一张图表来说明这一点。

标签: java design-patterns interface casting selenium-webdriver


【解决方案1】:

这不是new 的事情——它是Java 中的一种常见机制。看看IterableComparableSerializable。许多类将实现其中的一个或多个并且工作正常。

最近,随着泛型的出现,您可以使用这样的东西,它更有意义,并且不需要可怕的转换:

interface A {
  void doAStuff();

}

interface B {
  void doBStuff();

}

interface C {
  void doCStuff();

}

public static <T extends A & B & C> void doStuff (T to) {
  to.doAStuff();
  to.doBStuff();
  to.doCStuff();
}
  1. 这种层次结构是否符合设计模式?

    我不知道。

  2. 如果有,它有名字吗?

    见 1。

  3. 以这种方式设计 API 有哪些优点/缺点(而不是仅仅让 C 扩展 B,B 扩展 A)?

    如果接口不是层次结构,则没有理由使它们如此。请参阅上面的IterableComparable

【讨论】:

  • 我明白你在说什么,这是一个很好的观点。但我认为在 Selenium API 的情况下,情况有所不同。例如,所有 WebElement 都是具有坐标的事物,因此可以提出一个论点,即 WebElement 接口应该扩展 Locatable(具有 getCoordinates() 方法)。但是,事实并非如此。要获取 WebElement 上的坐标,必须首先交叉转换为可定位。这与 Iterable、Comparable 和 Serializeable 的情况不同,因为在很多情况下只需要实现其中之一。
  • 在我看来他们很懒惰。 OldCurmudgeon 的public static &lt;T extends A &amp; B &amp; C&gt; 方法看起来很有用。如果所有WebElement 实现必须能够getCoordinates(),那么是的,他们应该有WebElement 扩展Locatable。他们并不意味着并非所有WebElements 都是Locatable
  • @CorayThan 好点,因为并非所有 WebElement 都是可定位的暗示是错误的。
  • @CorayThan - 我不确定我是否会称他们为懒惰。我会说他们正在选择一种细粒度的接口方法。例如在Go 中有一个名为Readerinterface 有一个方法Read,只是io 包的一小部分。又是一种非常有效的细粒度方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-04
  • 2010-11-22
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 2011-01-13
  • 1970-01-01
相关资源
最近更新 更多