Подтвердить что ты не робот

"Открытые" вложенные классы или нет

Предположим, что у меня есть класс "Приложение". Для инициализации он принимает определенные настройки в конструкторе. Предположим также, что количество настроек настолько велико, что оно вынуждает их размещать в своем классе.

Сравните следующие две реализации этого сценария.

Реализация 1:

class Application 
{
   Application(ApplicationSettings settings) 
   {
       //Do initialisation here
   }
}

class ApplicationSettings 
{
   //Settings related methods and properties here
}

Реализация 2:

class Application 
{
   Application(Application.Settings settings) 
   {
       //Do initialisation here
   }

   class Settings 
   {
      //Settings related methods and properties here
   }
}

Для меня второй подход очень предпочтительнее. Это более читаемо, потому что оно сильно подчеркивает связь между двумя классами. Когда я пишу код для создания экземпляра класса Application в любом месте, второй подход будет выглядеть красивее.

Теперь представьте себе, что у самого класса настроек в свою очередь был некоторый аналогичный "родственный" класс, и этот класс в свою очередь тоже сделал это. Перейдите только к трем таким уровням, и название класса выйдет из-под контроля в "не-вложенном" случае. Однако, если вы гнездились, все по-прежнему остается изящным.

Несмотря на вышеизложенное, я читал людей, говорящих в StackOverflow, что вложенные классы оправданы, только если они не видны внешнему миру; то есть, если они используются только для внутренней реализации содержащего класса. Часто цитируемое возражение вызывает раздувание размера содержащего исходный файл класса, но частичные классы являются идеальным решением для этой проблемы.

Мой вопрос: почему мы опасаемся "публично выставленного" использования вложенных классов? Существуют ли другие аргументы против такого использования?

4b9b3361

Ответ 1

Думаю, все в порядке. Это в основном шаблон построителя, и использование вложенных классов работает очень хорошо. Он также позволяет строителю получить доступ к закрытым членам внешнего класса, что может быть очень полезным. Например, вы можете иметь метод Build в построителе, который вызывает частный конструктор внешнего класса, который принимает экземпляр компоновщика:

public class Outer
{
    private Outer(Builder builder)
    {
        // Copy stuff
    }

    public class Builder
    {
        public Outer Build()
        {
            return new Outer(this);
        }
    }
}

Это гарантирует, что единственный способ создания экземпляра внешнего класса - через конструктор.

Я использую шаблон, очень похожий на мой порт С# для протокольных буферов.

Ответ 2

Вы можете использовать пространства имен, чтобы связать вещи, которые... связаны.

Например:

namespace Diner
{
    public class Sandwich
    {
        public Sandwich(Filling filling) { }
    }

    public class Filling { }
}

Преимущество этого в использовании классов, как если бы они были пространствами имен, заключается в том, что вы можете использовать using на вызывающей стороне, чтобы сокращать вещи:

using Diner;

...

var sandwich = new Sandwich(new Filling());

Если вы используете класс Sandwich, как если бы это было пространство имен для Filling, вы должны использовать полное имя Sandwich.Filling для ссылки на Filling.

И как вы собираетесь спать по ночам, зная это?

Ответ 3

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

Ответ 4

В основном я использую вложенные классы для точной настройки доступа к вложенному и/или контейнерному классу.

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

Вы также можете использовать это для управления использованием определенного класса.

Пример:

public abstract class Outer
{
  protected class Inner
  {
  }
}

Теперь в этом случае пользователь (вашего класса) может получить доступ только к внутреннему классу, если он реализует Outer.

Ответ 5

Еще один практический пример, который я применил для использования публичных вложенных классов, находится в шаблоне MVC, когда я использую viewmodel с свойством IEnumerable. например:

public class OrderViewModel
{
public int OrderId{ get; set; }
public IEnumerable<Product> Products{ get; set; }

public class Product {
public string ProductName{ get; set; }
public decimal ProductPrice{ get; set; }
}

}

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