Какие-нибудь подходящие образцы для этой проблемы? - PullRequest
0 голосов
/ 22 июня 2009

У меня есть ситуация, в которой я пытаюсь сохранить свою модель и реализацию как можно более слабосвязанными, однако я сталкиваюсь с ситуацией, когда связь может потенциально стать намного ближе, чем я хочу.

У меня есть выбор классов 'Model', все реализующие интерфейсы. Кроме того, у меня есть классы «Доступ к данным», которые предоставляют ряд функций, одна из которых - декодирование целочисленных значений поиска в их полное представление «объекта».

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

Упрощенный пример:

/// Core classes --

class Car : ICar
{
    public int MakeId { get {...} set { ... } }

    public IMakeInfo Make { get {...} }

    public string Registration { get { ... } set { ... } }

    public int CurrentOwnerId { get { ... } set { ... } }

    public IPerson CurrentOwner { get { ... } }
}

class MakeInfo : IMakeInfo
{
    public string Name { ... }
    public int Id { ... }
    public decimal Weight { ... }
    // etc etc
}

/// Data Access Classes --

class ResolveMake 
{
    public IMakeInfo GetMakeInfo(int id)
    { 
        // Implementation here...
    }

}

Как включить класс Car для предоставления объекта IMakeInfo любым потребляющим классам без непосредственного информирования его о классе ResolveMake? В действительности, я работаю с классом Car, который не находится в том же пространстве имен, что и класс ResolveMake, и он не содержит ссылок ни на какие его экземпляры.

Некоторые из моих вариантов:

  • Реализация делегата в Car, который может быть снабжен экземпляром метода GetMakeInfo.
  • Какая-то инъекция зависимостей
  • Близко соедините Car с ResolveMake и покончите с этим.
  • Есть ли другие варианты?

Любые предложения приветствуются!

Ответы [ 5 ]

1 голос
/ 22 июня 2009

Звучит как инъекция зависимости для меня. Я делал аналогичные вещи с MS PP Unity и инжекцией как в конструктор, так и в метод и свойство. Тогда ваш класс Car будет иметь какую-то инъекцию IMakeInfo ... пример:

[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
  this.MakeInfo = makeInfo;
}
1 голос
/ 22 июня 2009

Методы расширения ?

namespace CarStuff
{
   class Car : ICar
   {
      public int MakeId { get {...} set { ... } }
      // no Make property...
      public string Registration { get { ... } set { ... } }
      public int CurrentOwnerId { get { ... } set { ... } }
      public IPerson CurrentOwner { get { ... } }
   }
}


namespace MakeExts
{
   class ResolveMake
   {
      public static IMakeInfo Make(this Car myCar)
      {
         //implementation here
      }
   }
}

в другом месте:

using MakeExts;

Car c = new Car();
Console.WriteLine(c.Make().ToString());

Редактировать: для использования методов расширения в .NET 2.0 вам нужно что-то вроде:

В основном, класс, содержащий:

namespace System.Runtime.CompilerServices 
{ 
   class ExtensionAttribute : Attribute
   {
   }
 }

и «использование System.Runtime.CompilerServices», разбросанных по соответствующим местам.

0 голосов
/ 23 июня 2009

В конце (учитывая ограничения, над которыми я работал) я фактически использовал вариацию на ряд тем выше.

Поскольку мне пришлось избегать любых ссылок на мой уровень доступа к данным из-за циклических ссылок, я в итоге сделал что-то с делегатами и «фабричным» кодом. Вписав его в свой исходный сценарий, я сделал следующее:

class Car 
{
    public void SetLookupProvider(ILookupProvider value) { _lookupProvider = value; }

    public IMakeInfo Make { get { return _lookupProvider.ResolveMake(MakeId); } }

    ....
}

interface ILookupProvider
{
    IMakeInfo ResolveMake(int id);
}

class LookupProvider
{
    public delegate IMakeInfo ResolveMakeDelegate(int id);

    public ResolveMakeDelegate ResolveMakeDel { set { _resolvemake = value; } }

    public IMakeInfo ResolveMake(int id){ return _resolvemake(id); }  
}

Тогда по моему заводскому методу ...

ICar returnedObject = new Car(blah, foo, bar, etc);

ILookupProvider luprovider = new LookupProvider();
luprovider.ResolveMakeDel = DataAccessLayer.FunctToGetMakeInfo;

(Car)returnedObject.SetLookupProvider(luprovider).

Теперь я первый, кто признает, что это не самое красивое решение (я бы выбрал методы Extension, если бы у меня был доступ к компилятору 3.0 ...), но он не связывает класс Car с уровнем DataAccess (что в моей ситуации предотвратило круговую ссылку ада ...). Класс Car не должен знать, как он получает результат, и фабричный метод, который в первую очередь создает объекты Car, - это единственное, что связано со слоем доступа к данным.

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

0 голосов
/ 22 июня 2009

Продолжая вашу аналогию, Car, очевидно, должен знать, когда get_Make будет называться IMakeInfo. Я также считаю, что это ключевая характеристика автомобиля. Поэтому я думаю, что передача IMakeInfo конструктору Car (возможно, с использованием IOC) очень разумна. Если это хорошая аналогия с вашим реальным кодом (у каждого «автомобиля» есть свой собственный «IMakeInfo»), я бы с этим согласился.

Вы также можете использовать сеттер, как сказал Йохан, опять же с опциональным IOC. Причина, по которой я предпочел бы конструктор, заключается в том, что каждый «Автомобиль» должен иметь «Make».

0 голосов
/ 22 июня 2009

Поскольку у класса Car есть свойство Make, которое возвращает IMakeInfo, похоже, что оно уже предоставляет информацию. Так я прав, если предположить, что ваша проблема заключается не только в том, как автомобиль получает возвращаемое значение?

Если это так, возможно, вам стоит взглянуть на создание фабричного метода , который знает об автомобилях и ResolveMake.

...