使用抽象类型变量中的具体类

时间:2015-04-14 18:14:50

标签: java testing selenium abstract-class concreteclass

我很抱歉,如果已经提出这个问题,我还没有找到类似我的问题......

我正在工作/玩/学习建立某种测试环境......在其中,我正在构建一个应用层(一个类包,它们是不同页面/窗口/表单的虚拟表示一个申请。简化的设置如下:

public abstract class WebPage {

    protected WebDriver driver;

    protected WebElement getElement(By by){
        WebElement element = (new WebDriverWait(driver, 10))
              .until(ExpectedConditions.presenceOfElementLocated(by));

    return element;
}

    public void menuLogout(){
        System.out.println("Logged out");
    }
}

public class HomePage extends WebPage {

    public ProfilePage ClickLinktoProfilePage(){
        return new ProfilePage();
    }

    public DashBoardPage clickViewDashboard(){
        return new DashBoardPage();
    }

    public String getTitle(){
        return getElement(By.id("title")).getText();
    }
}

public class ProfilePage extends WebPage {

    public String getUsername(){
        return getElement(By.id("name")).getText();
    }

    public String getEmail(){
    return getElement(By.id("email")).getText();
}       
    public HomePage clickReturnToHomePage(){
        return new HomePage();
    }

}

public class DashBoardPage extends WebPage {

    public String getcurrentPeriod(){
        return getElement(By.id("name")).getText();

}
}

这背后的想法是我希望我的测试只能保存一个当前的WebPage。我不希望每次更改页面时都创建一个新变量。

我也不想被提前知道我要去哪个页面。我希望应用程序层为我提供应用程序的流程。与点击链接时一样,您将进入下一页,我希望当我点击链接将我带到另一个页面时,该方法会告诉我我要进入的页面。

(WebPage抽象类还公开了所有具体WebPages之间的许多共享方法)

所以我的预期用途是:

WebPage currentPage = new HomePage();

currentPage = currentPage.ClickLinktoProfilePage(); //currentPage = new ProfilePage();
System.out.println(currentPage.getUsername());
currentPage.menuLogout();

可悲的是,这不起作用,因为currentPage变量被键入为WebPage,它无法看到任何具体类的方法。我发现它同时是逻辑和奇怪的,因为我可以问“currentPage.getClass()。getName();”它将返回“packageName.ConcreteClassName”。

要使Typecasting工作,我需要重新定义变量的类型......(不确定它是否可行或甚至好)。

所以我知道我可以在变量中找到类的名称,但我不知道从那里去哪里。

有人有解决方案吗?

2 个答案:

答案 0 :(得分:2)

在此处的评论中澄清Radiodef和我所说的内容:

你想要的是定义WebPage(你的抽象API),使你的具体子类不需要来拥有不属于那个的公共方法API。

例如,比较标准库中的java.util.List接口。此接口有多种实现(ArrayListLinkedList是最知名的,但还有很多其他实现),但使用List的大多数代码都不需要关心它是否实际使用ArrayListLinkedList或其他内容,因为您需要的所有操作都通过List接口公开。

您可以对WebPage课程执行相同的操作。例如,您可以为网页可以执行的不同操作定义一系列“挂钩”:

public abstract class WebPage {
  // methods that each subclass needs to implement
  protected abstract String renderBodyHtml();
  public abstract String getNameToLinkTo();

  // other methods that are common to every page
  public final void serve(
      HttpServletRequest request, HttpServletResponse response) {
     // write the response, using the specific page's body HTML
     response.getWriter().println(renderToBodyHtml());
  }
}

然后您的网页会实现合同,如下所示:

// Note: the class doesn't need to be public, since anybody that uses
// it can just declare their variable as type WebPage
class Page1 extends WebPage {
  @Override protected String renderBodyHtml() {
    return "<body>Hello world!</body>";
  }

  @Override public String getNameToLinkTo() {
     return "Page1";
  }
}

然后想要使用WebPage的代码不需要知道它是Page1(或任何其他页面):

public static void printPageName(WebPage webPage) {
  System.out.println(webPage.getNameToLinkTo());
}

或者,就像resueman所说,您可以直接使用Page1Page2等类型,仅使用WebPage实现继承,而不是API。这也很好 - 正确的解决方案取决于您希望代码的灵活性(和复杂性)。

答案 1 :(得分:1)

您可以通过强制类型调用具体类型的方法,如下所示:

WebPage currentPage = new Page1();
currentPage = ((Page1)currentPage).clickLink();
((Page2)currentPage).printHello();

但是,这很可能是解决问题的错误方法。通过让抽象类包含这些方法可以更好地处理这个问题,这样就可以在不进行强制转换的情况下调用它们,或者让变量成为具体类型。现在你正在编写声称接受任何WebPage的代码,但实际上需要特定类型,删除静态类型通常会给你的类型安全性。

在您的情况下,似乎没有任何理由使变量成为WebPage。首先,它必须是,并且已知是Page1。然后,它必须是Page2,再次,你知道它是一个。因此,正确的解决方案是准确反映您的类型:

Page1 currentPage = new Page1();
Page2 linkedPage = currentPage.clickLink();
linkedPage.printHello();

这是完全类型安全的,并且使代码更加自我记录。

如果这不起作用,那么您应该看看是否可以将不同的行为表示为您使用WebPage定义的api的特化。来自Daniel Prydencomment on another answer

  

最好以最一般的术语定义API,并允许该API的实现专门化。基本上,WebPage(抽象类)应该公开一个足够灵活的API,即客户端代码(它不知道它正在使用哪种WebPage)可能需要的任何操作。