2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




 
 "Фабрика по записи" в C# и Delphi
Сообщение16.02.2012, 22:38 
Ну, не уверен, что это дело называется "фабрикой по записи", но я для себя это дело именую именно так. Суть лучше проиллюстрирую на Delphi-коде:

код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
Type
  TThing = Class
    // whatever fields and methods

    Constructor Create(Const aS: String); Virtual;
  End;
  TThingClass = Class Of TBase;

  TThingLink = Record
    ID: Integer;
    ClassType: TThingClass;
  End;

  TThingFactory = Class
  Private
    fData: Array Of TThingLink;
    fCount, fSize: Integer;
  Public
    Constructor Create;
    Destructor Destroy; Override;
   
    Procedure AddLink(aID: Integer; aType: TThingClass);
    Function Build(aID: Integer; aS: String): TThing;
  End;

Implementation

Constructor TThingFactory.Create;
Begin
  fCount := 0;
  fSize := 10;
  SetLength(fData, fSize);
End;

Destructor TThingFactory.Destroy;
Begin
  SetLength(fData, 0);
End;
   
Procedure TThingFactory.AddLink(aID: Integer; aType: TThingClass);
Var
  I: Integer;
Begin
  I := 0;
  While (I < fCount) And (fData[I].ID <> aID) Do Inc(I);
  If I < fCount
  Then Raise Exception.Create('Duplicate ID: ' + IntToString(aID) + '; classes ' +
    fData[ID].ClassType.ClassName + ' and ' + aType.ClassName);

  If fCount = fSize
  Then Begin
    fSize := 2*fSize;
    SetLength(fData, fSize);
  End;

  With fData[fCount] Do Begin
    ID := aID;
    ClassType := aType;
  End;
  Inc(fCount);
End;

Function TThingFactory.Build(aID: Integer; aS: String): TThing;
Var
  I: Integer;
Begin
  I := 0;
  While (I < fCount) And (fData[I].ID <> aID) Do Inc(I);

  If I = fCount
  Then Raise Exception.Create('No thing with such ID')
  Else Result := fData[I].ClassType.Create(aS);
End;
 


Это самый-самый примитивный вариант, конечно же. Такая штуковина полезна, например, разбирать пришедшие по сети пакеты: скажем, если в заголовке стоит нолик, то это InitSessionMessage, если единичка — AuthorisationRequestMessage, и т.д. Наследуем все сообщения от одного базового класса, запихиваем в фабрику, каждое сообщение поддерживает конструктор, который инициализирует поля из сырого куска байтов... и все. Пришел пакет — вызвали MessageFactory.Build(Packet.ID, Packet.Data), и получили распарсенное сообщение.

Появились новые сообщения — написали новый класс, дописали строчку с MessageFactory.Add(100500, NewMessage.ClassType), и все. Это вообще может быть сделано во внешнем подключаемом модуле, о которым ты вообще не знаешь, потому что его писали уже пользователи/доработчики напильником по месту внедрения.

Собсно, вопрос — а как такая штуковина поизящней делается на C#? Там что-то тоже было такое, но может, есть какие тонкости? В Delphi, например, классу надо иметь виртуальный конструктор, иначе может не взлететь.

P.S. "Фабрикой по записи" я это называю потому, что каждый класс сам по желанию "записывается" на фабрику.

 
 
 
 Re: "Фабрика по записи" в C# и Delphi
Сообщение17.02.2012, 14:02 
Joker_vD в сообщении #539587 писал(а):
Собсно, вопрос — а как такая штуковина поизящней делается на C#?
Изящество -- вещь довольно субъективная, но я бы, наверное, сделал как-нибудь так:
код: [ скачать ] [ спрятать ]
Используется синтаксис C#
  1. namespace Topic55236
  2. {
  3.     public abstract class Thing
  4.     {
  5.         public abstract void SayHello();
  6.     }
  7.  
  8.     public class ThingFactory
  9.     {
  10.         private Dictionary<int, Type> fData = new Dictionary<int, Type>();
  11.  
  12.         public Thing Build(int id, string val)
  13.         {
  14.             if (!fData.ContainsKey(id))
  15.                 throw new ArgumentException("No thing with such Id");
  16.             Type type = fData[id];
  17.             ConstructorInfo ci = type.GetConstructor( new Type[] { typeof(string) } );
  18.             Thing result = (Thing) ci.Invoke(new object [] {val});
  19.             return result;
  20.         }
  21.  
  22.         public void AddLink(int aId, Type aType)
  23.         {
  24.             if (fData.ContainsKey(aId)) {
  25.                 Type t = fData[aId];
  26.                 throw new ArgumentException(
  27.                     string.Format("Duplicate ID: {0}; classes {1} and {2}", aId, t.Name, aType.Name));
  28.             }
  29.             if (!aType.IsSubclassOf(typeof(Thing)))
  30.                 throw new ArgumentException("Type must be subclass of Thing");
  31.             if (null == aType.GetConstructor(new Type[] { typeof(string) }))
  32.                 throw new ArgumentException("Type must be instantiable from string");
  33.             fData.Add(aId, aType);
  34.         }
  35.     }
  36.  
  37.     //====================================================
  38.     //  Ну а теперь посмотрим, как вся эта штука взлетит
  39.     //====================================================
  40.  
  41.     // Плохой класс: не наследуется от Thing
  42.     class Bad1
  43.     {
  44.     }
  45.  
  46.     // Плохой класс: нет конструктора из строки
  47.     class Bad2 : Thing
  48.     {
  49.         public override void SayHello()
  50.         {
  51.         }
  52.     }
  53.  
  54.     // Хороший класс
  55.     class Good : Thing
  56.     {
  57.         private string fString;
  58.         public Good(string aString)
  59.         {
  60.             fString = aString;
  61.         }
  62.         public override void SayHello()
  63.         {
  64.             Console.WriteLine("I'm good. String is " + fString);
  65.         }
  66.     }
  67.  
  68.     // Еще один хороший класс
  69.     class Good2 : Thing
  70.     {
  71.         public Good2(string aString)
  72.         {
  73.         }
  74.         public override void SayHello()
  75.         {
  76.         }
  77.     }
  78.  
  79.     class Program
  80.     {
  81.         static void Main(string[] args)
  82.         {
  83.             var thingFactory = new ThingFactory();
  84.             TryAdd(thingFactory, 1, typeof(Bad1));
  85.             TryAdd(thingFactory, 1, typeof(Bad2));
  86.  
  87.             thingFactory.AddLink(1, typeof(Good));
  88.             Thing thing = thingFactory.Build(1, "Message");
  89.             thing.SayHello();
  90.  
  91.             TryAdd(thingFactory, 1, typeof(Good2));
  92.         }
  93.  
  94.         static void TryAdd(ThingFactory factory, int id, Type type)
  95.         {
  96.             try {
  97.                 factory.AddLink(id, type);
  98.             }
  99.             catch (Exception ex) {
  100.                 Console.WriteLine(ex.Message);
  101.             }
  102.         }
  103.     }
  104. }
При запуске под VS2010 результат такой:
Код:
Type must be subclass of Thing
Type must be instantiable from string
I'm good. String is Message
Duplicate ID: 1; classes Good and Good2

 
 
 [ Сообщений: 2 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group