2014 dxdy logo

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

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




Начать новую тему Ответить на тему
 
 "Фабрика по записи" в C# и Delphi
Сообщение16.02.2012, 22:38 
Заслуженный участник


09/09/10
3729
Ну, не уверен, что это дело называется "фабрикой по записи", но я для себя это дело именую именно так. Суть лучше проиллюстрирую на 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 
Заслуженный участник


09/08/09
3438
С.Петербург
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 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group