2015-12-16 2 views
0

У меня есть три класса: AbstractContext, ContextA и ContextB, которые я хочу обобщить с точки зрения создания, но я хочу получить доступ к конкретным методам каждого из них в разных контекстах.Получить специализированный класс от Abstract Factory

AbstractContext:

public abstract class AbstractContext { 

    public abstract void print(); 
} 

ContextA:

public class ContextA extends AbstractContext { 

     @Override 
     public void print() { 
      System.out.println("In context A"); 
     } 

     public void doSomeA(){ 
      System.out.println("Do some A"); 
     } 
    } 

ContextB:

public class ContextB extends AbstractContext { 

     @Override 
     public void print() { 

     } 

     public void doSomeB(){ 
      int a=1; 
      System.out.println(a); 
     } 

    } 

Я реализовал абстрактную модель фабрики, так что я мог бы обобщать создание этих классов:

AbstractFactory:

public abstract class AbstractFactory { 

    public abstract AbstractContext createContext(); 

} 

FactoryA:

public class FactoryA extends AbstractFactory { 

    @Override 
    public AbstractContext createContext() { 
     AbstractContext newClass = new ContextA(); 
     return newClass; 
    } 

} 

FactoryB

public class FactoryB extends AbstractFactory { 

    @Override 
    public AbstractContext createContext() { 
     AbstractContext newClass = new ContextB(); 
     return newClass; 
    } 

} 

FactoryMaker

public class FactoryMaker { 
    private static AbstractFactory factory = null; 

    public static AbstractFactory getFactory(String condition) { 
     if (condition == "A") { 
      factory = new FactoryA(); 
     } else { 
      factory = new FactoryB(); 
     } 

     return factory; 
    } 
} 

Проблема здесь в том, после того, как экземпляр будет создан, так как все заводы в отставке urn базового типа я не могу получить доступ к каким-либо конкретным методам каждого подкласса.

public class Main { 

    public static void main(String[] args) { 

     AbstractContext contextA = FactoryMaker.getFactory("A").createContext(); 
     contextA.print(); //Works fine 
     contextA.doSomeA(); //Won't compile 

     AbstractContext contextB = FactoryMaker.getFactory("").createContext(); 
     contextB.print(); //Works fine 
     contextB.doSomeB(); //Won't Compile 

    } 
} 

На первые попытки, я пытался создать методы с той же сигнатурой, принимающей различные подтипами класса, но потом я получил ошибку компиляции, так как базовый тип не соответствует конкретным типам методов ожидать:

public static void process(ContextA context){ 
     context.doSomeA(); 
} 

public static void process(ContextB context){ 
     context.doSomeB(); 
} 

Есть ли способ достичь того, что я пытаюсь сделать?

Чтобы предоставить вам дополнительный контекст, я создаю общую библиотеку Java (API), которая будет использоваться другими разработчиками, то, что я пытаюсь сделать, - найти способ, которым разработчики имеют контекст, который они хотят предоставить в стандартный путь от моего API. Я хочу, чтобы они запрашивали контекст, и работа с этой точки заполняет свойства, которые каждый контекст имеет, тогда api будет обрабатывать определенные части каждого контекста.

EDIT: Похоже, я должен дать вам дополнительную информацию.

Представьте, что у меня есть два веб-приложения, оба веб-приложения получают доступ к тем же бизнес-классам услуг, но могут иметь разные бизнес-правила, потому что это две разные компании. Каждая компания имеет варианты одного и того же продукта.

То, что я пытаюсь достичь, - это сделать API, который внутренне сделает тяжелый подъем и общий код для разработчиков.

Что нужно сделать разработчикам: api, например, продукт и компания, а api возвращает правильный контекст и инициализирует контекст в соответствии с бизнес-правилами.

Я хочу, чтобы они «почти» строили только gui, чтобы заполнить каждый контекст, который я возвращаю. Конечно, им нужно будет знать контекст, с которым они работают, чтобы сделать привязки между графическим интерфейсом и контекстом, который я возвращаю, но в то же время их графический интерфейс может иметь общие части пользовательского интерфейса.

Обновление вещей на моем конце тоже, на данный момент, что я сделал, это создать ContextHolder, который возвращает контекст, который хочет разработчик, но он выглядит не очень хорошо, потому что компания может расширить линейку продуктов или вариации каждого продукта.

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

Я стараюсь изо всех сил объяснить себя, это недостаточно ясно, пожалуйста, дайте мне знать.

+1

Вы написали, что «разработчикам не нужно думать, какой контекст они создают». Но если каждый контекст имеет собственные конкретные методы, разработчики должны думать о каком-то контексте –

+1

Единственный способ, который я вижу, - это Generics: Abstract Factory будет иметь специализированный контекст как общий параметр. Проблема с этим заключается в том, что для Factorymaker нужен специальный класс для создания специального Factory ... – Mikey

+0

Alexey, я хочу, чтобы мой API предоставлял общий способ получить контекст, не создавая для каждого пользователя моего API фабрик на их сторонах, чтобы получить контекст, который они хотят. Вы правы, они должны думать о каком-то контексте, но я хочу, чтобы они имели это стандартным образом из моего api. То, что я пытаюсь достичь, может быть невозможным :) –

ответ

0

Это может быть вариант:

public interface Context { 

    void print(); 

} 


public class ContextHolder<T extends Context> { 

    private T context; 

    public ContextHolder(T context) { 
     this.context = context; 
    } 

    public T getContext() { 
     return context; 
    } 

    public void print() { 
     context.print(); 
    } 

    public void foo() { 

    } 

} 


public class ContextA implements Context { 

    @Override 
    public void print() { 
     System.out.println("In context A"); 
    } 

    public void doSomeA() { 
     System.out.println("Do some A"); 
    } 
} 


public class ContextB implements Context { 

    @Override 
    public void print() { 

    } 

    public void doSomeB() { 
     int a = 1; 
     System.out.println(a); 
    } 

} 

public abstract class AbstractFactory<T extends Context> { 

    public abstract ContextHolder<T> createContext(); 

} 

public class FactoryA extends AbstractFactory<ContextA> { 

    @Override 
    public ContextHolder<ContextA> createContext() { 
     return new ContextHolder<ContextA>(new ContextA()); 
    } 

} 

public class FactoryB extends AbstractFactory<ContextB> { 

    @Override 
    public ContextHolder<ContextB> createContext() { 
     return new ContextHolder<ContextB>(new ContextB()); 
    } 

} 

public class FactoryMaker { 

    private static AbstractFactory factory = null; 

    public static AbstractFactory getFactory(String condition) { 
     if (condition == "A") { 
      factory = new FactoryA(); 
     } else { 
      factory = new FactoryB(); 
     } 

     return factory; 
    } 
} 


public static void main(String[] args) { 
     ContextHolder<ContextA> contextHolderA = FactoryMaker.getFactory("A").createContext(); 
     contextHolderA.print();  
     contextHolderA.getContext().doSomeA(); 

     ContextHolder<ContextB> contextHolderB = FactoryMaker.getFactory("").createContext(); 
     contextHolderB.print(); 
     contextHolderB.getContext().doSomeB(); 
    } 
0

Я думаю, что у вас есть проблемы проектирования. Завод возвращает AbstractContext, но этого явно недостаточно для клиентов. Я бы предложил либо передумать API AbstractContext, либо ввести интерфейс. В обоих случаях вы должны найти абстрактный api для обоих контекстов.

В противном случае .... Если вы не можете идти по этому пути вы должны воссозданию информации утраченную типа либо Используйте

Visitor Pattern подход

public interface ContextVisitor { 
     public void visit(ContextA contextA); 
     public void visit(ContextB contextB); 
} 

public abstract class AbstractContext { 
    public abstract void print(); 
    public abstract void accept(ContextVisitor contextVisitor); 
} 

public class ContextA extends AbstractContext { 
    .... 
    public void accept(ContextVisitor contextVisitor){ 
     contextVisitor.visit(this); 
    } 
} 

шаблон использования

ContextVisitor cv = new ContextVisitor(){ 

    public void visit(ContextA contextA){ 
     contextA.print(); 
     contextA.doSomeA(); 
    } 

    public void visit(ContextB contextB){ 
     contextB.print(); 
     contextB.doSomeB(); 
    } 

} 

AbstractContext contextA = FactoryMaker.getFactory("A").createContext(); 
contextA.accept(cv); 
AbstractContext contextB = FactoryMaker.getFactory("").createContext(); 
contextB.accept(cv); 

Adapter Pattern подход

public abstract class AbstractContext { 
    public abstract void print(); 
    public abstract <T extends AbstractContext> T getAdapter(Class<T> type); 
} 


public class ContextA extends AbstractContext { 
    .... 
    public <T extends AbstractContext> T getAdapter(Class<T> type) { 
     if(type.isInstance(this)){ 
      return type.cast(this); 
     } 
     return null; 
    } 
} 

адаптер использование шаблон:

AbstractContext contextA = FactoryMaker.getFactory("A").createContext(); 
ContextA contextA = contextA.getAdapter(ContextA.class); 
if(contextA != null){ 
    contextA.print(); 
    contextA.doSomeA(); 
} 

Если оба подхода не относятся к вашей ситуации вы можете использовать только instanceof чеки и слепков для восстановления информации о потерянном типе. Но это не является предпочтительным способом, поскольку вы перемещаете выбор типа в клиентский код, и его можно быстро сломать, если вы измените иерархию наследования.

0

Я думаю, вы выбрали слишком сложный инструмент для работы.

Если вы просто хотите «гарантировать каждый контекст инициализируется таким же образом, и не имея разработчиков сделать разные initilization себя», инкапсуляции достаточно.

Этот базовый объектно-ориентированный принцип позволяет скрыть детали реализации внутри объекта, который, кажется, именно то, что вам нужно.

Предполагая, что ContextA еще продолжается AbstractContext, сравните это (1)

ContextA contextA = new ContextA(); 
contextA.print(); //Works fine 
contextA.doSomeA(); //Works fine 

с предыдущим кодом (2)

AbstractContext contextA = FactoryMaker.getFactory("A").createContext(); 
contextA.print(); //Works fine 
contextA.doSomeA(); //Won't compile 

и расскажите, пожалуйста, что в (2), который не мог» (1), потому что в текущем состоянии вашего вопроса и примеров я не вижу его.

+0

Пожалуйста, подготовьте мое обновленное сообщение. 2 может абстрагировать инициализацию класса, 1 всегда будет инициализировать класс таким же образом. –

+0

Err, no. 'new ContextA()' будет инициализировать класс в 'ContextA',' new ContextB() 'способом' ContextB' и т. д. – guillaume31

+0

, кроме того, я упомянул, что 'ContextA' все еще расширяет' AbstractContext', поэтому вы можете положить общая логика инициализации там тоже, если вы хотите. – guillaume31

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