Вопрос

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

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

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

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

/// 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 и покончите с этим.
  • Есть еще варианты?

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

Это было полезно?

Решение

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

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», разбросанное в соответствующих местах.

Другие советы

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

[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
  this.MakeInfo = makeInfo;
}

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

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

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

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

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

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

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).

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top