2012-03-30 10 views
9

Wiem, że nie robię tego dobrze, ale wiem też, że jest sposób, aby to zrobić. Staram się być jak najbardziej ogólna i abstrakcyjna, w przeciwnym razie mój kod stanie się naprawdę brudny. Używam tutaj strategii jako piekła, która jest metodą GetAggregateClient().Najlepszy sposób na zrobienie tej ogólnej klasy abstrakcyjnej w języku C#?

Chcę mieć klasę abstrakcyjną o nazwie AbstractAggregate, tak aby używała generycznych. Typ, który będzie używany, to szereg klas danych, które są BlogItem, ResourceItem i AskItem. Wszystkie te klasy danych dziedziczą z ListItem.

To są podstawowe informacje. Problem polega na tym, że chcę, aby GetAbstractAggregate() zwrócił instancję jednej z klas klientów, która implementuje AbstractAggregate, z określonym typem elementu, w zależności od podanego parametru enum. Nie mogę jednak zwrócić "AbstractAggregate". Kompilator mi nie pozwoli i ma to sens, ponieważ klasa AbstractAggregateFactory nie jest generyczna.

Czy ktoś ma najlepszy sposób na zrobienie tego?

Wielkie dzięki.

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 
} 

Odpowiedz

3

problem, kompilator narzeka
... jest to, że masz sposób, który jest " open "(T) - i wracasz do zamkniętego generycznego (z <AskItem> itd.), typem konkretnym naprawdę.
tj. Trzeba zwrócić ... <T> - ... i można to zrobić za pomocą metody - bez względu na to, czy fabryka nie jest generyczna, metoda może być nieruchoma.
Co to jest najlepszy sposób,
to pytanie "projektowe" - i nieco dłuższa historia,
Nie jestem do końca pewien, co próbujesz osiągnąć (może jakaś historia w tle, jak wiele typów, które możesz mieć itp.),

Po pierwsze, nie powinieneś (ogólnie mówiąc, jako najlepsza praktyka lub jakiś czynnik "dobrze się czujesz")
odziedzicz swoje przedmioty z ListItem - użyj innej klasy bazowej swojej - i jeśli potrzebujesz kolekcji, użyj generycznego takiego jak List<T> - lub stwórz własną implementację IList itd.

Po drugie, nie potrzebujesz wszystkiego, co ogólne. Twój podstawowy agregator jest generyczny, ale niestandardowe klasy zwykle nie są np. tak ...

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; 
    } 
} 

... to jedna z realizacji.
Zasadniczo nie wszystko "ogólne" jest zawsze najlepszym sposobem. Musisz mieć wystarczające powody lub "typy", które mogą być nieznane. Podobnie jak w przypadku leków generycznych, płacisz również pewną cenę. Przekraczanie generyków do świata nietypowego jest często trudne i wymaga refleksji, jeśli typów nie można wywnioskować na podstawie użycia itp.
Błąd IMO sprawia, że ​​każdy dostawca jest ogólny - ponieważ akceptuje tylko jeden typ (każdy konkret), podczas gdy baza jest ogólna. Tak jak powyżej. Zwykle ogólny jest również ograniczony dla interfejsu gdzie/gdzie możesz.
Ale wtedy masz problem z powrotem do ogólnego kontekstu z klasą, która nie jest generyczna, nie jest prosta (masz też na myśli pewne zastrzeżenia z typami wartości, które trzeba czasem traktować inaczej, często) i imadłem versa również.
Dlatego najpierw potrzebujesz czegoś takiego jak cast (obiekt).
Wolałbym używać tutaj rodzaju podejścia do IOC - np. spójrz na autofac (nie jestem związany, ale podoba mi się, jak to działa, ładne ramy). W takim przypadku, że zrobisz coś takiego ...

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

     // and query later... 

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

nadzieję, że to pomoże trochę ...

+0

dzięki za szczegółowe wyjaśnienie. początkowo wybrałem inną odpowiedź, ale mnie przekonałeś. dzięki za pomoc! – apexdodge

+0

np :) - jest wiele sposobów/odpowiedzi, po prostu trzymaj się logiki (każdy bit zwykle ma swój cel lub nie gwarantuje miejsca) i wszystko będzie dobrze. I spójrz na IOC, autofac, spodoba ci się (więcej niż generics :) – NSGaga

1

Nie jestem pewien, czy rozumiem, co chce osiągnąć, ale być może jest to coś takiego

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

Nie sądzę, można zrobić to BlogItem 'T' - trzeba wartość T - lub 'typeof (T) .equals (typeof (BlogItem))' lub coś - tak jak sidenote – NSGaga

+0

@NSGaga: Tak, prawdopodobnie masz rację. – Gebb

1

Staram się być tak ogólne i abstrakcyjne, jak to możliwe, inaczej moje kod będzie się bałagan.

Jest to nieporozumienie. bycie generycznym/abstrakcyjnym może faktycznie skomplikować prosty prosty problem. Kluczem do oczyszczenia kodu jest enkapsulacja. znacznie różni się od dziedziczenia lub generycznych.

W tym przypadku myślę, że kompozycja byłaby lepszym wyborem niż dziedziczeniem. przy użyciu zestawu adapterów można mieć wspólny obiekt, do którego można dopasować każdą jednostkę. na przykład:

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

Jak o tym:

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 
} 

Przykład:

AbstractAggregate<BlogItem> foo3 = 
    AbstractAggregateFactory.GetAggregateClient<BlogItem>(AggregateHelper.AggregateTypes.AskTankTruck); 
foo3.GetSingle("test"); 
0

Jedno jest ewentualnie jasne jest to, że projekt jest nieco wadliwa. Typ przełączania nie jest najlepszą rzeczą do zrobienia w ogólnej metodzie, która pokonuje jego cel. Ale nie jest jasne, jaki jest cel twoich zajęć.

Niektóre spekulacje:

1) widząc klas pary AskItem i AskTankTruckAggregate<T> etc nie sądzę, ten ostatni musi być rodzajowe klasy, jest to bardzo specyficzna klasa, ściśle sprzężony AskItem. Chciałbym zaprojektować go jak

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 
} 

nazwać to lubią:

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

2) inny sposób: przekazać łączną tworzenie miejsc pracy do 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 
} 

nazwać to lubią:

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

3) lub takie same, ale sprawiają, że nieco silniej wpisane, z wykorzystaniem leków generycznych:

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 
} 

połączeń to:

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

4) Na koniec może być mniej typowy typ zwrotu? Obejmuje przypadek przełącznika, nie podoba mi się to.

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 
} 

nazwać to lubią:

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

Imo, podejście to jest gorsze niż podejście refleksji. Łatwo zapomnieć o wyliczeniach w przyszłości.


Ze wszystkich, trzeci wygląda najlepiej w moich oczach, ale znowu nie znając swojego projektu, jest bardzo trudny do przewidzenia. Kilka sugestii:

  1. Twoja nazwa fabryczna brzmi lepiej jak AggregateFactory. "Abstrakcja" w nim sprawia, że ​​bardziej na temat realizacji.

  2. W przypadku potrzeby wyliczenia typu, nie należy go zagnieżdżać. Zagnieżdżone typy publiczne są trudniejsze do wywołania. Wyjmij pakującą klasę statyczną (jak w moim piątym podejściu).

  3. Zmień nazwę swojej klasy bazowej na Aggregate<T> lub . Ponownie "abstrakcja" w nim sprawia, że ​​bardziej na temat realizacji, zupełnie niepotrzebne.

Powiązane problemy