我很抱歉,如果已经提出这个问题,我还没有找到类似我的问题......
我正在工作/玩/学习建立某种测试环境......在其中,我正在构建一个应用层(一个类包,它们是不同页面/窗口/表单的虚拟表示一个申请。简化的设置如下:
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工作,我需要重新定义变量的类型......(不确定它是否可行或甚至好)。
所以我知道我可以在变量中找到类的名称,但我不知道从那里去哪里。
有人有解决方案吗?
答案 0 :(得分:2)
在此处的评论中澄清Radiodef和我所说的内容:
你想要的是定义WebPage
(你的抽象API),使你的具体子类不需要来拥有不属于那个的公共方法API。
例如,比较标准库中的java.util.List
接口。此接口有多种实现(ArrayList
和LinkedList
是最知名的,但还有很多其他实现),但使用List
的大多数代码都不需要关心它是否实际使用ArrayList
或LinkedList
或其他内容,因为您需要的所有操作都通过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所说,您可以直接使用Page1
,Page2
等类型,仅使用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 Pryden的comment on another answer,
最好以最一般的术语定义API,并允许该API的实现专门化。基本上,WebPage(抽象类)应该公开一个足够灵活的API,即客户端代码(它不知道它正在使用哪种WebPage)可能需要的任何操作。