2016-02-12 4 views
0

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

class EntityFOO 
{ 
    EntityFOO LoadFromXml(XDocument) {...} 
    EntityFOO LoadFromDB(string) {...} 
} 

class EntityBAR 
{ 
    EntityBAR LoadFromXml(XDocument) {...} 
    EntityBAR LoadFromDB(string) {...} 
} 

Я хотел бы, чтобы реорганизовать и извлечь интерфейс. Я хотел бы что-то вроде этого:

abstract class Entity 
{ 
    static string DatabaseConnectionString = "shared_across_implementations"; 

    abstract Entity LoadFromXml(XDocument); 
    abstract Entity LoadFromDB(string); 
} 

class EntityBAR : Entity 
{ 
    EntityBAR LoadFromXml(XDocument) {...} 
    EntityBAR LoadFromDB(string) {...} 
} 

Обратите внимание, что реализации Entity, вернуть свой собственный тип EntityBAR, а не родительская Entity. И предположим, что мне придется как-то использовать <Generics>.

ответ

2

Нечто подобное ?:

abstract class Entity<T> where T : Entity<T> 
{ 
    static string DatabaseConnectionString = "shared_across_implementations"; 

    abstract T LoadFromXml(XDocument); 
    abstract T LoadFromDB(string); 
} 

class EntityBAR : Entity<EntityBAR> 
{ 
    EntityBAR LoadFromXml(XDocument) {...} 
    EntityBAR LoadFromDB(string) {...} 
} 

Непроверено конечно, и что Entity<T> where T : Entity<T> баловаться с моей интуиции, делая меня, что это какой-то странной проблемы типа рекурсии. Но я не думаю это проблема.

Хотя меня беспокоят еще те методы LoadFrom..., которые у вас есть. Если это заводы на самом типе, не будут ли они static? Что изменило бы схему наследования. Мне кажется странным, что для создания экземпляра понадобится экземпляр.

Возможно, конструкторы с параметрами string и XDocument имели бы смысл для этого? И так как конструкторы не имеют типов возвращаемых данных, это как бы делает вопрос спорным. Что-то структурно как это (также руки и непроверенное и рода псевдо-код-иш):

abstract class Entity 
{ 
    static string DatabaseConnectionString = "shared_across_implementations"; 

    protected Entity(XDocument) {...} 
    protected Entity(string) {...} 
} 

class EntityBAR : Entity 
{ 
    public EntityBAR(XDocument) : base(XDocument) {...} 
    public EntityBAR(string) : base(string) {...} 
} 

Если, конечно, операция строительства/инициализация объекта не очень тяжелый и сам включает в себя другими зависимостей. В этом случае конструктор не всегда является безопасным местом для этого, и дизайн будет требовать отдельных объектов Factory. Эти могут быть основаны на экземплярах и возвращаться Эти экземпляров.

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

+0

Кажется, что нужно построить ОК, если я положу 'public' на методы и' переопределяет '' методы класса и переменные в подписях. На самом деле не ожидал, что это будет возможно: o) – NikolaiDante

+0

@NikolaiDante: Я думаю, что сам дизайн может использовать некоторые улучшения, которые делают сам вопрос спорным. Немного подумал об этом и обновил ответ. – David

+0

Эти 'Load *', не являются фабриками, они фактически не создают объекты (create), они просто устанавливают поля данного объекта (load). Ваше решение работает, даже без 'where T'. – dialex

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