2015-04-14 6 views
0

Я извиняюсь, если этот вопрос уже спросил, я не нашел ничего подобного мой вопрос еще ...Использование конкретного класса из абстрактного типа переменной

Я работаю/игры/обучения, чтобы построить в какой-то среде тестирования ... Внутри я создаю Application Layer (пакет классов, являющийся виртуальным представлением разных страниц/окон/форм) приложения. Упрощенная настройка заключается в следующем:

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. Я не хочу создавать новую переменную каждый раз, когда меняю страницу.

Я также не хочу, чтобы меня заставляли заранее знать, на какой странице я направляюсь. Я хочу, чтобы Application Layer дал мне поток приложения. Точно так же, когда при нажатии ссылки вы попадаете на следующую страницу, я хочу, чтобы при нажатии ссылки, которая выводит меня на другую страницу, этот метод сообщает мне, на какой странице я направляюсь.

(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

Используйте конкретный тип, т.е. 'Page1' или литой? –

+1

Вы пытаетесь вызвать либо 'clickLink()', либо 'printHello()' в зависимости от фактического типа? Тип вставляется в локальную переменную или переписывает родительский класс (или интерфейс), чтобы потребовать метод абстрактного void doSomething() и переопределить абстрактное поведение в каждом дочернем классе. – ryanyuyu

+1

@ roux69: Образцы кода недействительны Java. В этом случае я думаю, что все здесь могут понять, что вы имеете в виду, но лучше использовать примеры кода, которые на самом деле демонстрируют вашу проблему. Дополнительную информацию см. На странице http://stackoverflow.com/help/mcve. –

ответ

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(); 

Это совершенно типобезопасно, и делает код более самодокументируемым.

Если это не сработает, вы должны увидеть, может ли другое поведение выражаться как специализация api, которую вы определяете с помощью WebPage.Из Daniel Pryden «s comment on another answer,

Это гораздо лучше, чтобы определить API в самых общих чертах возможных, и позволить реализации этого API специализироваться. В принципе, WebPage (абстрактный класс) должен предоставить достаточно гибкий API, который может потребоваться для любой операции, которую клиентский код (который не знает, какой WebPage, с которым он работает).

+1

+1 для «это скорее всего неправильное решение проблемы». Вероятно, было бы целесообразно расширить вторую половину вашего ответа, так как я думаю, что это действительно правильный ответ для OP. –

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

@LocHa: Если у вас 1000 подклассов, я надеюсь, что вы можете определить API, который является общим для всех из них. (Так как в противном случае это означает, что у вас есть 1000 специальных веб-страниц для снежинок, это означает, что ваш сайт уже является незаменимым беспорядком.) Но теперь мы перешли к теме дизайна API - возможно, вам стоит открыть новый вопрос? –

+2

@LocHa: Вы серьезно утверждаете, что ваша открытая поверхность API должна быть 1000 различных методов? Если у вас есть много методов, даже отражение будет громоздким, так как вам нужно будет закодировать логику, чтобы выбрать способ вызова. Без API, чтобы дать смысловой смысл методам, вы не можете * априори * знать, какой метод вызывать. Конечно, вы можете создавать API-интерфейсы с утиным шрифтом, где вы ищете метод с конкретным именем или сигнатурой (см. JUnit 3), но если вы делаете это, вы гораздо лучше с аннотациями. Ваш аргумент (и downvote) здесь не имеет смысла. –

+1

@ DanielPryden Thx для подробного объяснения. Я все это уже знал. И чем больше я читаю вас, ребята, тем больше я понимаю, что моя проблема может быть не такой, как мне кажется ... в основном ... Возможно, я смотрел на это неправильно ... что я пытался использовать абстрактные классы таким образом, который никогда не должен был быть ... может быть? – roux69

Смежные вопросы