Заводская настройка. класс объекта - как это сделать красиво? - PullRequest
2 голосов
/ 08 марта 2010

В моем текущем проекте у нас есть пара классов данных, которые имеют дело с основными понятиями нашей прикладной области. Теперь в некоторых местах нашего проекта мы должны вести себя по-разному в зависимости от конкретного объекта в руке. Например. JList имеет задачу рендеринга списка объектов, но мы хотим, чтобы рендеринг был немного другим, в зависимости от класса объекта. Например. объект класса A должен отображаться иначе, чем объект класса B, а класс C также является совершенно другим животным.

Мы инкапсулируем поведение в классах стратегии, а затем имеем фабрику, которая возвращает класс, подходящий для объекта, который должен быть визуализирован. С точки зрения клиента, это нормально, я думаю.

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

if (obj instanceof classA) return strategyA;
else if (obj instanceof classB) return strategyB;
...

Теперь для пула уже созданных объектов карта также будет работать. Но если фабрика должна фактически создать новый объект, нам нужно будет поместить на эту карту еще один слой объектов фабрики / стратегии, который затем вернет подходящие стратегии для отображения.

Есть ли шаблон проектирования, который хорошо справляется с такой проблемой?

Ответы [ 4 ]

1 голос
/ 09 марта 2010

Мне очень нравятся объекты фабричной стратегии обслуживания. Но мне интересно, могли бы вы относиться к этому как к IoC и регистрировать стратегии для конкретных типов? У вас нет кучки if-else, но вам придется их «зарегистрировать». Но это также может быть полезно для тестирования - скорее, для реализации «фиктивной фабрики», которую вы регистрируете «фиктивные стратегии»?

1 голос
/ 09 марта 2010

Вместо блока if / else у вас может быть интерфейс Factory, как этот

interface RendererFactory {
   supports(Object obj);
   createRenderer(Object obj);
}

Тогда у вас может быть реализация, которая запрашивает список других реализаций, если одна из них поддерживает данный тип. Другие реализации могут выполнять проверку instanceof в методе supports. Потребителю рендерера нужно только позвонить createRenderer.

Преимущество: возможна конфигурация вашего RendererFactories

Недостаток: Вы должны позаботиться о порядке RendererFactories (но вы должны сделать это с if / else тоже)

1 голос
/ 08 марта 2010

Один из способов сделать это - делегировать реализацию самому объекту. Например, если все классы A, B и C отображаются по-разному, каждый из них может иметь интерфейс, такой как:

interface IRenderable {
    public void render();
}

Тогда каждый из них предоставит свою собственную реализацию render(). Чтобы отобразить List из IRenderable, вам нужно будет только перебрать его элементы и вызвать метод render() каждого из них.

Используя этот подход, вам никогда не придется явно проверять тип объекта. Это особенно полезно, если любой из ваших классов когда-либо разделен на подклассы. Предположим, у вас был класс classD, который расширяет classA и должен был отображаться иначе, чем A. Теперь код как:

if (obj instanceof classA) return strategyA;
...
else if (obj instanceof classD) return strategyD;

не удастся - вам всегда нужно проверять порядок от наиболее до наименее конкретных. Лучше не думать о таких вещах.

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

Примерно так:

class Renderer {
    public void visit(classA obj);
    public void visit(classB obj);
    // etc
}

и

class classA {
    public void accept(Renderer r) {
        r.visit(this);
    }
}

Теперь весь код рендеринга переходит в Renderer, и объекты модели выбирают, какой метод вызывать.

0 голосов
/ 08 марта 2010

Ваши классы моделей могут реализовывать интерфейс, подобный:

public interface RenderingStrategyProvider {
    public RenderingStrategy getRenderingStrategy();
}

и вернуть экземпляр соответствующей стратегии. Как:

public ClassA implements RenderingStrategyProvider {
    public RenderingStrategy getRenderingStrategy() {
        return new ClassARenderingStrategy(this); 
        // or without this, depending on your other code
    }
}

В таком случае вам даже не понадобится фабрика. Или, если вы хотите, он будет содержать только один вызов метода. Таким образом, у вас нет логики представления в ваших модельных классах.

В качестве альтернативы, вы можете использовать соглашение + отражение, но это странно. Стратегия для модельного класса будет ModelClassStrategy, и вы можете иметь:

public RenderingStrategy createRenderingStrategy(Object modelObject) {
    return (RenderingStrategy) Class.forName(
          modelObject.getClass().getName() + "Strategy").newInstance();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...