2016-08-02 1 views
3

У меня есть сборка .NET, которую я создаю, и я хочу, чтобы конечный пользователь мог видеть только несколько классов и методов. Я создаю сборку с проектом класса библиотеки в рамках 4.5 с Visual Studio 2015. Сборка предоставит консоль управления для управления некоторыми взаимосвязанными бэкэнд-услугами и приложениями. Большая часть работы будет происходить в сборке и будет невидимой для конечного пользователя. Большинство классов в пространстве имен сборки будут внутренними и поэтому невидимыми для конечного пользователя; но сборка предоставит несколько общедоступных классов и методов, с которыми может взаимодействовать конечный пользователь. Как я это себе представляю, сборка предоставит открытый статический класс, в котором конечный пользователь будет передавать параметры, необходимые сборке, а затем вернуть объект, который может использовать конечный пользователь. Например, вот шаблон псевдокода того, что этот открытый статический класс может выглядеть концептуально.Как реализовать инъекцию зависимостей Setter от внутренних модификаторов доступа

public static class ControlClientStarter 
{ 
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */) 
    { 
     // Some code that parses the parameters . . . 

     ISrvc_Controller accountController = new AccountSrvc_Controller(/*. . . */); 
     ISrvc_Controller contractController = new ContractSrvc_Controller(/*. . . */); 
     ISrvc_Controller imageController = new ImageSrvc_Controller(/*. . . */); 
     ICatalogApp_Controller calatlogController = new CatalogApp_Controller(/*. . . */); 
     IPortal_Controller portalController = new PortalSrvc_Controller(/*. . . */); 

     ISrvcManagerConsole srvcMngrConsole = new SrvceManagerConsole(
      accountController, 
      contractController, 
      imageController, 
      calatlogController, 
      portalController); 

     return srvcMngrConsole; 
    } 
} 

Код выше показывает, что сборка .dll обеспечивает публичный статический класс с именем ControlClientStarter, который имеет один публичный статический метод GetSrvcManagerConsole() (...). Этот статический метод принимает несколько параметров, а затем на основе этих параметров и различной логики создает объекты, которые реализуют интерфейс ISrvc_Controller, а также другие объекты, которые реализуют другие интерфейсы. Эти объекты вводятся в конструктор объекта srvcMngrConsole и возвращают это конечному пользователю.

Конечный пользователь получит доступ к управляющей консоли с чем-то концептуально похожее на это,

ISrvcManagerConsole clientMngrConsole = ControlClientStarter.GetSrvcManagerConsole( /*. . . */); 
clientMngrConsole.DoThis( /*. . . */); 
clientMngrConsole.StopThis( /*. . . */); 
clientMngrConsole.GetThat( /*. . . */); 
clientMngrConsole.PublishThat( /*. . . */); 

Внутри консоли управления может выглядеть отвлеченно, как этот псевдокод, где аргументы, переданные в SrvcManagerConsole спасаются интерфейс реализации поля внутри объекта srvcMngrConsole,

internal class SrvcManagerConsole : ISrvcManagerConsole 
{ 
    ISrvc_Controller accountController ; 
    ISrvc_Controller contractController ; 
    ISrvc_Controller imageController ; 
    ICatalogApp_Controller calatlogController; 
    IPortal_Controller portalController ; 

    internal SrvcManagerConsole(
     ISrvc_Controller accountController, 
     ISrvc_Controller contractController, 
     ISrvc_Controller imageController, 
     ICatalogApp_Controller calatlogController, 
     IPortal_Controller portalController)    
    { 
     this.accountController = accountController; 
     this.contractController = contractController; 
     this.imageController = imageController; 
     this.calatlogController = calatlogController; 
     this.portalController = portalController; 
    } 

    public void DoThis(/*. . . */) 
    { /*. . . */ } 
    public void StopThis(/*. . . */) 
    { /*. . . */ } 
    public void GetThat(/*. . . */) 
    { /*. . . */ } 
    public void PublishThat(/*. . . */) 
    { /*. . . */ } 

    // private and-or internal methods and properties . . . 
} 

Как вы можете видеть, я использую инъекции в конструкторе зависимостей, чтобы избежать сильной связи; но есть проблема с этим. Что делать, если в будущем я хочу добавить MapPublishApp_Controller, VideoSrvc_Controller и т. Д.?

Если я добавлю их в конструктор после первой версии, тогда другой код, который уже использует эту сборку, также должен измениться. Если я добавлю еще один конструктор SrvceManagerConsole с дополнительными параметрами, которые берут новые контроллеры, то я не буду нарушать предыдущий код, но параметры конструктора начнут становиться слишком многочисленными. Кроме того, в зависимости от параметров, которые конечный пользователь отправляет методу GetSrvcManagerConsole() класса ControlClientStarter, я, возможно, не захочу использовать все контроллеры. Было бы намного лучше, если бы у меня были методы, которые добавляют контроллеры в поля. Задача состоит в том, что я не хочу, чтобы конечный пользователь имел доступ к этим полям. Я хочу, чтобы сборка создавала экземпляры классов контроллера и назначала скрытые поля. Если я дам эти методы присваивания в модификаторах внутреннего доступа класса SrvceManagerConsole, метод GetSrvcManagerConsole() не сможет их увидеть, потому что он является общедоступным.

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

internal interface ISrvc_Controller { /*. . . */ } 
internal interface ICatalogApp_Controller { /*. . . */ } 
internal interface IPortal_Controller { /*. . . */ } 

public interface ISrvcManagerConsole 
{ 
    void DoThis(/*. . . */); 
    void StopThis(/*. . . */); 
    void GetThat(/*. . . */); 
    void PublishThat(/*. . . */); 
} 

Эти методы, как выше PublishThat() будет использовать поля, которые получили установленные на объекты, инжектированных в конструктор.Программист конечного пользователя, который использует мою сборку, никогда не увидит эти интерфейсы, реализующие экземпляры. Объект clientMngrConsole, созданный программистом конечного пользователя, будет использовать эти внутренние объекты без программиста, даже зная, как они используются.

Классы, реализующие интерфейсы с модификаторами доступа к внутренним бы отвлеченно быть что-то вроде этого,

class AccountSrvc_Controller : ISrvc_Controller { /*. . . */ } 
class ContractSrvc_Controller : ISrvc_Controller { /*. . . */ } 
class ImageSrvc_Controller : ISrvc_Controller { /*. . . */ } 
class CatalogApp_Controller : ICatalogApp_Controller { /*. . . */ } 
class PortalSrvc_Controller : IPortal_Controller { /*. . . */ } 

Там может быть несколько классов контроллеров в будущем, может быть, нет. Мне не удалось выяснить, как назначить классы контроллеров для задних полей таким образом, чтобы эти поля были недоступны для конечного пользователя, сохраняя при этом некоторый уровень ослабленной связи, поскольку выполнение этих контроллеров необходимо изменить в будущее. Я обнаружил, однако, что, если другие методы и свойства указаны в интерфейсе ISrvceManagerConsole, и этот интерфейс задан с помощью общедоступного модификатора, эти методы и свойства в классе SrvceManagerConsole, который реализует этот интерфейс, также могут быть установлены как общедоступные и успешно достигнутый, несмотря на то, что сам класс SrvceManagerConsole задан как «внутренний». Я попробовал установку setterter, но C# (мудро) не поддерживает свойство с интерфейсом backend (потому что это не имеет никакого смысла). Другая возможность состоит в том, чтобы конструктор SrvceManagerConsole использовал один параметр, который, возможно, обертывает все другие экземпляры интерфейса в словаре. Я еще не пробовал. Я не могу быть первым, кто столкнулся с этим. Должно быть общее решение, которое я не смог найти. Я знаю, что отраслевым стандартом является использование контейнеров IoC для того, что я делаю выше, но это не будет сделано здесь. Я никогда раньше не пробовал такую ​​архитектуру. Я знаю, что это, вероятно, не лучшая архитектура; но пока это не должно быть точно в соответствии с некоторой чрезмерно сложной архитектурой. Я собираюсь создать адекватный дизайн, поскольку я больше об этом узнаю.

Edit # 1, почему шаблон Builder не работает здесь

Предположим, что я пытаюсь избежать конструктора антишаблоном, используя шаблон строитель, как показано ниже псевдокоде.

Представьте, что SrvcManagerConsole выглядит следующим образом:

internal class SrvcManagerConsole : ISrvcManagerConsole 
{ 
    internal ISrvc_Controller accountController; 

    internal SrvcManagerConsole() { } 

    internal void AddAccountController(ISrvc_Controller accountController) 
    { 
     this.accountController = accountController; 
    } 
} 

Представьте изменить ControlClientStarter на что-то вроде этого:

public static class ControlClientStarter 
{ 
    public static ISrvcManagerConsole GetSrvcManagerConsole(/*. all the parameters . */) 
    { 
     Federated_ControlBuilder fedBuilder = new Federated_ControlBuilder(); 
     ISrvcManagerConsole srvcMngrConsole = fedBuilder.GetSrvcManagerConsole(); 
     return srvcMngrConsole; 
    } 
} 

И тогда я реализовать Federated_ControlBuilder так:

internal class Federated_ControlBuilder : IControl_Builder 
{ 
    internal ISrvcManagerConsole srvcMngrConsole; 

    internal Federated_ControlBuilder() 
    { 
     srvcMngrConsole = new SrvcManagerConsole(); 
    } 

    public void InjectAccountController(ISrvc_Controller accountController) 
    { 
     // The srvcMngrConsole object can not see the InjectAccountController method. 
     // srvcMngrConsole. 
    } 

    internal ISrvcManagerConsole GetSrvcManagerConsole() 
    { 
     return srvcMngrConsole; 
    } 
} 

Метод InjectAccountController (...) для Federated_Contr Класс olBuilder не может видеть метод AddAccountController (...) класса SrvcManagerConsole, потому что он является внутренним.

Точка зрения моего вопроса заключается не в том, как избежать конструктора анти-шаблона сами по себе; но задача состоит в том, чтобы избежать анти-шаблона, делая определенные классы и методы в сборке недоступными для конечного пользователя-программиста, следовательно, использование слова «внутренний» заголовок вопроса «Как реализовать зависимость сеттера Инъекция на внутренних Модификаторы доступа.«

Если бы это был всего лишь вопрос об избегании конструктора анти-шаблона, я мог бы публиковать открытые поля SrvcManagerConsole и добавлять их непосредственно через метод GetSrvcManagerConsole (...) класса ControlClientStarter.

ответ

0

Я бы порекомендовал использовать the builder pattern, чтобы избежать конструкции телескопического конструктора. Затем ваш код должен только добавлять сеттеры для реализации новых функций, и вы оставляете свой конструктор пустым.

+0

Благодарим вас за рекомендацию. Это ценно. Я ответил на мой исходный пост после раздела «Редактировать # 1 ...». – Beebok

0

Я не знаю, является ли это хорошим решением или нет, но, похоже, он работает над тем, чего я пытался добиться. Чтобы повторить, мне нужна была сборка, где внутренняя работа была бы недоступна, если бы модификаторы доступа к их классам были установлены на внутренние, но предоставили бы некоторые общедоступные классы, которые предоставили бы функции консольного пользовательского интерфейса тем, кто будет использовать сборку. Проблема заключалась в том, что если публичные классы имели задние конечные поля, которые были установлены с помощью модификаторов внутреннего доступа, другие внутренние классы не могли вводить значения в эти поля. Моим решением было создать два пространства имен в сборке. Одно пространство имен будет иметь все классы, установленные для модификатора внутреннего доступа. Это классы внутренней логики сборки. Второе пространство имен будет иметь общедоступные классы. У него будет оператор using, который ссылается на первое пространство имен. Эти публичные классы во втором пространстве имен могут затем запрашивать объекты из классов в первом пространстве имен. Затем классы во втором пространстве имен обертывают эти объекты и предоставляют оболочку конечному пользователю.

Вот пример из первого пространства имен сборки:

// This is the first namespace with all classes set to internal. 
namespace ControlClient 
{ 
    internal class ClientStarter 
    { 
     internal IArcManagerConsole mngrConsole; 

     internal ClientStarter() { } 

     internal IManagerConsole GetSrvcManagerConsole(/*. all the parameters . */) 
     { 
      ConsoleBuildDirector cnslBldDirector = new ConsoleBuildDirector(); 

      IConsoleBuilder fedBuilder = new Federated_ConsoleBuilder(); 
      cnslBldDirector.DirectBuildingConsole(fedBuilder); 

      mngrConsole = fedBuilder.GetSrvcManagerConsole(); 
      return mngrConsole; 
     } 
    } 
} 

Вот пример из второго пространства имен сборки с общественными классами доступных конечным программистом.

using ControlClient; // referencing the first name space 

// This is the second namespace with public classes. 
namespace ClientUserInterface 
{ 
    /// <summary> 
    /// SrvcManagerConsole wraps the mngrConsole. 
    /// They both implement the IManagerConsole interface. 
    /// </summary> 
    public class SrvcManagerConsole 
    { 
     IManagerConsole srvcManager; 

     public SrvcManagerConsole() 
     { 
      // this.srvcManager = srvcManager; 
      ClientStarter ccs = new ClientStarter(); 
      this.srvcManager = ccs.GetSrvcManagerConsole(); 
     } 

     // The DoThis() method of this wrapper class will 
     // call the DoThis() method of the object from the 
     // first namespace that implements the same 
     // interface. The end user can't see the 
     // srvcManager object. 
     public void DoThis() 
     { 
      srvcManager.DoThis();    
     } 
    } 
} 

Вот пример из проекта конечного пользователя.

using ClientUserInterface; // This is the second namespace 
using ControlClient; // This is the first namespace (all classes set to internal). 

namespace EndUser 
{ 
    class EndUser 
    { 
     public EndUser() 
     {  
      // The end user can access public classes from the assembly's 
      // second namespace, ArcClientUserInterface as seen below.  
      SrvcManagerConsole sMC = new SrvcManagerConsole(); 
      sMC.DoThis();  

      // Trying to access anything here from the assembly's 
      // first namespace, ArcControlClient will fail. 
      // Now, all the inner workings of the assembly are undetectable. 
     } 
    } 
} 

Конечный пользователь добавит сборку к своим проектам, а затем попытается использовать оба пространства имён; но только второе пространство имен будет предоставлять элементы, к которым можно получить доступ. Конечный пользователь может использовать только классы в этом втором пространстве имен (включая обертку). Объект, полученный из первого пространства имен и управляемый оболочкой, недоступен конечному пользователю. Элементы первой сборки затем недоступны для проекта конечного пользователя и невидимы, потому что все они настроены на модификатор внутреннего доступа. Конечный пользователь манипулирует оболочкой, а оболочка, в свою очередь, манипулирует объектом, полученным из первого пространства имен. Итак, способ внедрения инъекции зависимостей setter от модификаторов внутреннего доступа состоит в том, чтобы все классы в одном пространстве имен были установлены в внутренние, которые будут участвовать в инъекции зависимостей сеттера. Затем укажите второе пространство имен, которое использует объект из первого пространства имен, обертывает его, а затем предоставляет публичные классы программистам конечных пользователей. Любая обратная связь и советы будут оценены. Заранее спасибо.

+0

В одном из ваших комментариев кода вы говорите: «Оба они реализуют интерфейс IManagerConsole», но я не вижу этого в коде. Я слепой? В любом случае, это было интересно читать, поскольку я боролся с объединением инъекции зависимостей, развязыванием сборок и скрытием реализации (ваши внутренние классы). Мысленно, я пришел к аналогичному выводу, что упаковщик/декоратор может быть хорошим способом обеспечения публичного использования с внутренней реализацией. – bubbleking