Лучший способ сделать этот общий абстрактный класс в C #? - PullRequest
9 голосов
/ 30 марта 2012

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

Я хочу иметь абстрактный класс с именем AbstractAggregate, чтобы он использовал дженерики. Тип, который будет использоваться, - это серия классов данных BlogItem, ResourceItem и AskItem. Все эти классы данных наследуются от ListItem.

Так что это фоновая информация. Проблема в том, что я хочу, чтобы GetAbstractAggregate () возвращал экземпляр одного из клиентских классов, который реализует AbstractAggregate с типом элемента, указанным в зависимости от переданного перечисления. Однако я не могу вернуть «AbstractAggregate». Компилятор не позволит мне, и это имеет смысл, поскольку класс AbstractAggregateFactory не является универсальным.

У кого-нибудь есть лучший способ сделать это?

Большое спасибо.

public static class AggregateHelper
{
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
}

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient(AggregateHelper.AggregateTypes type)
    {
        switch (type)
        {
            case AggregateHelper.AggregateTypes.AskTankTruck:
                return new AskTankTruckAggregate<AskItem>();
            case AggregateHelper.AggregateTypes.TankTruckBlog:
                return new TankTruckBlogAggregate<BlogItem>();
            case AggregateHelper.AggregateTypes.Resources:
                return new ResourcesAggregate<ResourceItem>();
            default:
                throw new AggregateDoesNotExistException();
        }
    }
}

public abstract class AbstractAggregate<T>
{
    public abstract List<T> GetAggregate(Guid[] resourcetypes);

    public abstract T GetSingle(string friendlyname);


}

public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

public class ResourcesAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

Ответы [ 5 ]

3 голосов
/ 31 марта 2012

проблема, компилятор жалуется на
... в том, что у вас есть метод, который является «открытым» (T) - и вы возвращаете закрытый универсальный (с <AskItem> и т. Д.) Конкретный тип.
т.е. вы должны вернуть ... <T> - ... и вы можете сделать это с помощью метода - независимо от того, является ли фабрика не универсальной, метод может быть все еще.
Что касается того, что лучшеспособ сделать это,
это больше вопрос "дизайна" - и немного более длинная история,
Я не совсем уверен, чего вы пытаетесь достичь (может быть, какая-то предыстория, сколько типов вы могли бы иметьи т.д.),

Во-первых, вы не должны (вообще говоря, как лучшая практика или какой-то «приятный» фактор)
наследовать ваши предметы от ListItem - использовать какой-то другой ваш базовый класс - иесли вам нужна коллекция, используйте обобщенную, например List<T>, или создайте собственную реализацию IList и т. д.

Во-вторых, вам не нужно все универсальное.Ваш базовый агрегатор является универсальным, но пользовательские классы, как правило, не такие, например, как это ...

abstract class ItemBase  { }
class AskItem : ItemBase { }
class BlogItem : ItemBase { }
class ProvderA : ProviderBase<AskItem>
{
    public override AskItem Get()
    {
        throw new NotImplementedException();
    }
}
class ProvderB : ProviderBase<BlogItem>
{
    public override BlogItem Get()
    {
        throw new NotImplementedException();
    }
}
abstract class ProviderBase<T> where T : ItemBase
{
    public abstract T Get();
}
class Program
{
    static void Main(string[] args)
    {
        ProviderBase<AskItem> provider = GetProvider<AskItem>();
        var item = provider.Get();
    }
    static ProviderBase<T> GetProvider<T>() where T : ItemBase
    {
        if (typeof(T) == typeof(AskItem))
            return (ProviderBase<T>)(object)new ProvderA();
        if (typeof(T) == typeof(BlogItem))
            return (ProviderBase<T>)(object)new ProvderB();
        return null;
    }
}

... это одна реализация.
По сути, не все "универсальные" всегда являются лучшим способом,У вас должно быть достаточно причин или «неизвестных» типов, чтобы их можно было использовать.Как и с дженериком, вы также платите определенную цену.Пересечение дженериков в мир, не связанный с дженериками, часто бывает сложным и требует размышлений, если ваши типы не могут быть выведены из использования и т. Д.
Ошибка IMO делает каждого провайдера универсальным - поскольку он принимает только один тип (каждый конкретный),в то время как база является общей.Так как выше.Обычно generic также ограничен для интерфейса, где / где вы можете.
Но тогда у вас возникает проблема, поскольку приведение обратно к универсальному контексту из фактически неуниверсального класса не является прямым (также следует иметь в виду, что есть предостережения с типами значений).так как иногда приходится обращаться с этим по-разному, часто), и наоборот.
Следовательно, сначала вам нужно что-то вроде cast (object).
Я бы предпочел использовать здесь своего рода подход IOC - например, посмотрите на autofac (я не связан, но мне нравится, как это работает, хороший фреймворк).В этом случае вы бы сделали что-то вроде ...

        container.Register<ProviderBase<AskItem>>(c=> new ProvderA());
        container.Register<ProviderBase<BlogItem>>(c => new ProvderB());

        // and query later...

        ProviderBase<AskItem> provider = container.Resolve<ProviderBase<AskItem>>();

надеюсь, это поможет некоторым ...

1 голос
/ 31 марта 2012

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

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

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

interface ICommon { ... }

class AskAdaptor: ICommon
{
    private readonly Ask ask;
    publick AskAdaptor(Ask ask)
    {
        this.ask = ask;
    }
}

class AskAdaptor: ICommon
{
    private readonly Blog blog;
    publick AskAdaptor(Blog blog)
    {
        this.blog = blog;
    }
}

class AskAdaptor: ICommon
{
    private readonly Resource resource;
    publick AskAdaptor(Resource resource)
    {
        this.resource = resource;
    }
}

class CommonAggregate
{
    public void Add(ICommon common)
    {
         ....
    }
}
1 голос
/ 31 марта 2012

Как насчет этого:

public static class AggregateHelper
{
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }
}

public class AskItem { }
public class BlogItem { }
public class ResourceItem { }

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient<T>
       (AggregateHelper.AggregateTypes type)
    {
        switch (type)
        {
            case AggregateHelper.AggregateTypes.AskTankTruck:
                return new AskTankTruckAggregate<T>();
            case AggregateHelper.AggregateTypes.TankTruckBlog:
                return new TankTruckBlogAggregate<T>();
            case AggregateHelper.AggregateTypes.Resources:
                return new ResourcesAggregate<T>();
            default:
                throw new ArgumentException();
        }
    }
}

public abstract class AbstractAggregate<T>
{
    public abstract List<T> GetAggregate(Guid[] resourcetypes);
    public abstract T GetSingle(string friendlyname);
}

public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
    public override List<T> GetAggregate(Guid[] resourcetypes)
    {
        throw new NotImplementedException();
    }

    public override T GetSingle(string friendlyname)
    {
        Console.WriteLine(friendlyname);
        Type whats_t = typeof(T);
        return default(T);
    }
}

public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

public class ResourcesAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

Пример:

AbstractAggregate<BlogItem> foo3 = 
   AbstractAggregateFactory.GetAggregateClient<BlogItem>(AggregateHelper.AggregateTypes.AskTankTruck);
foo3.GetSingle("test");
1 голос
/ 31 марта 2012

Я не уверен, что понимаю, чего вы пытаетесь достичь, но, возможно, это что-то вроде этого

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient<T>()
    {
        if(T is AskItem) return new AskTankTruckAggregate();
        if(T is BlogItem) return new TankTruckBlogAggregate();
        if(T is ResourceItem) return new ResourcesAggregate();
    }
}

public abstract class AbstractAggregate<T>
{
    public abstract List<T> GetAggregate(Guid[] resourcetypes);

    public abstract T GetSingle(string friendlyname);
}

public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
    //not implemented yet
}

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
    //not implemented yet
}

public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
    //not implemented yet
}
0 голосов
/ 17 января 2014

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

Некоторые предположения:

1) Просмотр ваших парных классов AskItem и AskTankTruckAggregate<T> и т. Д. Я не думаю, что последний должен быть универсальным классом, это очень специфический класс, тесно связанный с AskItem. Я бы изменил его как

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem
    {
        //use reflection to find the type that inherits AbstractAggregate<T>

        //instantiate the type

        //cast to AbstractAggregate<T> and return
    }
}

public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
    //not implemented yet
}

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
    //not implemented yet
}

public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
    //not implemented yet
}

Назовите это как:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc

2) Другой способ: делегировать задание по созданию совокупности вашим ListItems.

public abstract class ListItem //or interface
{
    protected abstract object Create();
} 
public class AskItem : ListItem { //implement to return AskTankTruckAggregate 
}
public class BlogItem : ListItem { //implement to return TankTruckBlogAggregate 
}
public class ResourceItem : ListItem { //implement to return ResourcesAggregate 
}

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
    {
        return (AbstractAggregate<T>)new T().Create();
    }
}

public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
    //not implemented yet
}

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
    //not implemented yet
}

public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
    //not implemented yet
}

Назовите это как:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc

3) Или то же самое, но сделайте его более строго набранным с использованием обобщений:

public abstract class ListItem<T> where T : ListItem<T> //or interface
{
    protected abstract AbstractAggregate<T> Create();
} 
public class AskItem : ListItem<AskItem> { //implement to return AskTankTruckAggregate 
}
public class BlogItem : ListItem<BlogItem> { //implement to return TankTruckBlogAggregate 
}
public class ResourceItem : ListItem<ResourceItem> { //implement to return ResourcesAggregate 
}

public static class AbstractAggregateFactory
{
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new()
    {
        return new T().Create();
    }
}

public class AskTankTruckAggregate : AbstractAggregate<AskItem>
{
    //not implemented yet
}

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem>
{
    //not implemented yet
}

public class ResourcesAggregate : AbstractAggregate<ResourceItem>
{
    //not implemented yet
}

Назовите это как:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc

4) Наконец, можно ли сделать тип возвращаемого значения менее универсальным? Включает случай переключения, мне не нравится.

public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources }

public static class AbstractAggregateFactory
{
    public static AbstractAggregate GetAggregateClient(AggregateTypes type)
    {
        switch (type)
        {
            case AggregateTypes.AskTankTruck:
                return new AskTankTruckAggregate<AskItem>();
            case AggregateTypes.TankTruckBlog:
                return new TankTruckBlogAggregate<BlogItem>();
            case AggregateTypes.Resources:
                return new ResourcesAggregate<ResourceItem>();
            default:
                throw new AggregateDoesNotExistException();
        }
    }
}

public abstract class AbstractAggregate
{

}

public abstract class AbstractAggregate<T> : AbstractAggregate
{

}

//or change the definition to AskTankTruckAggregate : AbstractAggregate<AskItem>
public class AskTankTruckAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

//or change the definition to TankTruckBlogAggregate : AbstractAggregate<BlogItem>
public class TankTruckBlogAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

//or change the definition to ResourcesAggregate : AbstractAggregate<ResourceItem>
public class ResourcesAggregate<T> : AbstractAggregate<T>
{
    //not implemented yet
}

Назовите это как:

AbstractAggregateFactory.GetAggregateClient(AggregateTypes.AskTankTruck); //etc

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


Из всех, 3-й выглядит лучше всего на мой взгляд, но опять же, не зная цели вашего дизайна, его очень сложно предсказать. Несколько предложений:

  1. Название вашей фабрики звучит лучше, чем AggregateFactory. «Аннотация» в нем делает больше о реализации.

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

  3. Переименуйте ваш базовый класс в Aggregate<T> или AggregateBase<T>. Опять же «абстрактный» в нем делает больше о реализации, совершенно ненужно.

...