C # ограничивающий использование класса - PullRequest
0 голосов
/ 13 июня 2019

Приведенный ниже код делает то, что я хотел бы.Код в методе Main выглядит и ведет себя точно так, как нужно.Однако было бы предпочтительным, если бы класс UserMenu, Home и DropdownMenu2 мог использоваться только HeaderNavigationMenu для защиты других разработчиков от попыток использовать их вне класса HeaderNavigationMenu.Кроме того, большинство статей недовольны тем, что все делается публично.

Вопрос : Правильно ли используется шаблон проектирования, указанный ниже, или есть что-то лучшее и более приемлемое для использования в этом сценарии?


Редактировать: Причинаэтот дизайн.

  1. Я хотел, чтобы конечный пользователь HeaderNavigationMenu мог просто использовать точечную запись, чтобы получить список доступных опций.Эта архитектура решает эту задачу (например: navigationMenu.DropdownMenu2.SelectOption3 ())
  2. Требуется любой, кому в конечном итоге может понадобиться изменить код, чтобы понять, что классы UserMenu, Home и DropDownMenu2 специально разработаны для реализации.по классу HeaderNavigationMenu.

public class HeaderNavigationMenu
{
    public HeaderNavigationMenu()
    {
        UsersMenu = new UsersMenu();
        Home = new Home();
        DropdownMenu2 = new DropdownMenu2();
    }

    public UsersMenu UsersMenu { get; set; }
    public Home Home { get; set; }
    public DropdownMenu2 DropdownMenu2 { get; set; }
}

public class UsersMenu
{
  ...
}

public class Home
{
  ...
}

public class DropdownMenu2
{
  public void SelectOption3()
  {
    ...
  }
  ...
}

static void Main(string[] args)
{
  HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu();
  navigationMenu.DropdownMenu2.SelectOption3();

  // The following code is an example of undesired capability; 
  // prefer if Home class could only be 
  // used by HeaderNavigationMenu class
  Home home = new Home();
}

Ответы [ 4 ]

3 голосов
/ 13 июня 2019

Ограничить доступ к конструкторам классов.Если они объявлены как «внутренние», то классы могут быть созданы только вашим кодом.

2 голосов
/ 13 июня 2019

Если вы ищете защиту от создания экземпляров UsersMenu, DropdownMenu2 и Home извне HeaderNavigationMenu, но все еще в рамках того же проекта, что и HeaderNavigationMenu, то есть хитрый трюк, который может достичь это поведение. Вы можете использовать публичные вложенные классы с закрытыми конструкторами, которые статически инициализируют свои собственные фабричные методы. Базовый шаблон для этого будет:

public class Outer{
  private static Func<Inner> _innerFactory;
  public Inner ExposedInner {get; private set;}

  public Outer(){
    // Force the static initializer to run.
    System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Inner).TypeHandle);

    // Call the newly created factory method instead of a regular constructor.
    ExposedInner = _innerFactory();
  }

  public class Inner {
    static Inner(){
      // Initialize Outer's static factory method.
      _innerFactory = () => new Inner();
    }

    // Inner cannot be instantiated (without reflection) because its constructor is private.
    private Inner(){}

    // This method is now exposed for anyone to use.
    public void DoStuff(){ Console.WriteLine("Did stuff"); }
  }
}

Вот эта концепция, реализованная в вашем примере:

class Program
{
    static void Main(string[] args)
    {
        HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu();
        navigationMenu.DropdownMenu2.SelectOption3();

        // This line will no longer work because the constructors
        // for the inner classes are private.
        HeaderNavigationMenu.HomeImpl home = new HeaderNavigationMenu.HomeImpl();

        Console.ReadKey();
    }
}

public class HeaderNavigationMenu
{
    //Private factory methods that are statically initialized
    private static Func<UsersMenuImpl> _createUsers;
    private static Func<DropdownMenu2Impl> _createDropdown;
    private static Func<HomeImpl> _createHome;

    public HeaderNavigationMenu()
    {
        //Force the static constructors to run
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(UsersMenuImpl).TypeHandle);
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(HomeImpl).TypeHandle);
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(DropdownMenu2Impl).TypeHandle);

        UsersMenu = _createUsers();
        Home = _createHome();
        DropdownMenu2 = _createDropdown();
    }

    public UsersMenuImpl UsersMenu { get; set; }
    public HomeImpl Home { get; set; }
    public DropdownMenu2Impl DropdownMenu2 { get; set; }

    public class UsersMenuImpl
    {
        //Static constructor to make the class factory method
        static UsersMenuImpl()
        {
            _createUsers = () => new UsersMenuImpl();
        }

        private UsersMenuImpl() { }
    }

    public class HomeImpl
    {
        //Static constructor to make the class factory method
        static HomeImpl()
        {
            _createHome = () => new HomeImpl();
        }

        private HomeImpl() { }
    }

    public class DropdownMenu2Impl
    {
        //Static constructor to make the class factory method
        static DropdownMenu2Impl()
        {
            _createDropdown = () => new DropdownMenu2Impl();
        }

        private DropdownMenu2Impl() { }

        public void SelectOption3()
        {
        }
    }
}

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

1 голос
/ 13 июня 2019

Вы можете создать классы UsersMenu, Home и DropdownMenu2 public abstract. Затем вложите в класс HeaderNavigationMenu классы private, которые расширяют версии public abstract.

public abstract class UsersMenu
{
}

public abstract class Home
{
}

public abstract class DropdownMenu2
{
   public void SelectOption3()
   {
      // Code for SelectOption3...
   }
}

public class HeaderNavigationMenu
{
    public HeaderNavigationMenu()
    {
        UsersMenu = new UsersMenuImpl();
        Home = new HomeImpl();
        DropdownMenu2 = new DropdownMenu2Impl();
    }

    public UsersMenu UsersMenu { get; }
    public Home Home { get; }
    public DropdownMenu2 DropdownMenu2 { get; }

    private class UsersMenuImpl : UsersMenu
    {
    }

    private class HomeImpl : Home
    {
    }

    private class DropdownMenu2Impl : DropdownMenu2
    {
    }
}

Разработчики могут видеть и использовать классы UsersMenu, Home и DropdownMenu2 abstract, но не могут создавать их экземпляры. Только HeaderNavigationMenu может.

Конечно, другой разработчик всегда может создавать свои собственные классы, основанные на public abstract, но вы можете сделать только так много. UsersMenu, Home и DropdownMenu2 должны быть public, чтобы иметь public свойства.

1 голос
/ 13 июня 2019

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

Если вы раскомментируете строку

//Home home = new Home();

вы получите ошибку компилятора.

class Program
{
    static void Main(string[] args)
    {
        HeaderNavigationMenu navigationMenu = new HeaderNavigationMenu();
        navigationMenu.DropdownMenu2SelectOption3();

        // The following code is an example of undesired capability; 
        // prefer if Home class could only be 
        // used by HeaderNavigationMenu class
        //Home home = new Home();
    }
}
public class HeaderNavigationMenu
{
    UsersMenu usersMenu;
    Home home;
    DropdownMenu2 dropdownMenu2;

    public HeaderNavigationMenu()
    {
        usersMenu = new UsersMenu();
        home = new Home();
        dropdownMenu2 = new DropdownMenu2();
    }

    public void DropdownMenu2SelectOption3()
    {
        dropdownMenu2.SelectOption3();
    }

    class UsersMenu
    {
    }

    class Home
    {
    }

    class DropdownMenu2
    {
        public void SelectOption3()
        {
        }
    }
}
...