Абстрактный дизайн фабрики шаблон и дженерики - Java - PullRequest
0 голосов
/ 27 января 2019

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

enter image description here

У меня есть два AbstractProduct , для которых я хотел бы создать фабрику, поскольку они принадлежат к разным «семьям». Один из продуктов представляет собой коллекцию другого.

public abstract class Frame{
    ...
}

public abstract class FrameMap<F extends Frame>{
    protected TreeMap<Integer, F> map;

    public F readByKey(Integer key){
        return this.map.get(key);
    }

    public void put(Integer key, F frame){
        this.map.put(key,frame);
    }
    ...
}

Обратите внимание, что FrameMap - это набор F (я упустил большинство других методов для простоты), поэтому он должен иметь возможность получать и размещать элементы. Если взять в качестве примера одну семью (одну для App1), это конкретный Product :

public class App1Frame extends Frame{
    ...
}

public class App1FrameMap extends FrameMap<App1Frame>{
    ...
}

Затем я создал AbstractFactory :

public abstract class ApplicationFactory{
    //Frame factory methods
    public abstract Frame makeFrame();

    //FrameMap factory methods
    public abstract FrameMap<? extends Frame> makeFrameMap();
}

И ConcreteFactory для App1:

public class App1Factory extends ApplicationFactory{
    //Frame factory methods
    public Frame makeFrame(){
        return new App1Frame();
    }

    //FrameMap factory methods
    public FrameMap<App1Frame> makeFrameMap(){
        return new App1FrameMap();
    }
}

Все это компилируется, но мне это не очень нравится, потому что мой Клиент не может сделать что-то вроде:

ApplicationFactory factory = new App1Factory();
FrameMap<Frame> frameMap = factory.makeFrameMap(); //This doesn't compile!

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

App1Factory factory = new App1Factory();
FrameMap<App1Frame> frameMap = factory.makeFrameMap(); //This compiles!

что противоречит цели использования шаблона проектирования.

Знаете ли вы какой-нибудь лучший способ реализации этого шаблона в такой ситуации, как у меня, то есть с использованием обобщений в AbstractProduct, чтобы позволить Клиенту использовать только абстрактные классы, не зная базовой реализации?

Ответы [ 2 ]

0 голосов
/ 28 января 2019

В дополнение к решению @ jaco0646, существует простой способ решения проблемы добавления недавно созданного фрейма на карту, даже если вы не знаете во время выполнения фактический подтип или «семейство».Это значит, что вы можете связать оба параметра типа frame и frame-map, чтобы они имели одно и то же значение в методе, например так:

public static <T extend Frame> FrameMap<T> makeAndAdd(final AbstractFactory<T> factory) {
    T frame = factory.makeFrame();
    FrameMap<T> map = factory.makeFrameMap();
    map.add(frame);
    return map;
}

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

0 голосов
/ 27 января 2019

Вы можете добавить универсальный тип к ApplicationFactory.

public class MainJava {
    interface Frame{}
    interface FrameMap<T extends Frame>{}

    static class App1Frame implements Frame{}
    static class App2Frame implements Frame{}
    static class App1FrameMap implements FrameMap<App1Frame>{}
    static class App2FrameMap implements FrameMap<App2Frame>{}

    interface ApplicationFactory<T extends Frame> {
        T makeFrame();
        FrameMap<T> makeFrameMap();
    }

    static class App1Factory implements ApplicationFactory<App1Frame> {
        @Override public App1Frame makeFrame() {
            return new App1Frame();
        }
        @Override public FrameMap<App1Frame> makeFrameMap() {
            return new App1FrameMap();
        }
    }

    public static void main(String... args) {
        ApplicationFactory<? extends Frame> factory = new App1Factory();
        Frame frame = factory.makeFrame();
        FrameMap<? extends Frame> map = factory.makeFrameMap();
    }
}

Также обратите внимание, что объект ApplicationFactory должен быть введен в клиент.Если клиент напрямую вызывает new App1Factory(), абстракция не имеет смысла.Клиент может также использовать App1Factory после его создания.

...