Инкапсуляция в эпоху фреймворков

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

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

В C# был сделан еще один шаг с инициализаторами объектов, которые дают вам возможность в основном определить свой собственный конструктор. (Я знаю, что инициализаторы объектов на самом деле не являются пользовательскими конструкторами, но я надеюсь, что вы понимаете мою точку зрения.)

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

ИЗМЕНИТЬ: I знаю, что вы все еще можете тщательно инкапсулировать члены, но я просто чувствую, что когда вы пытаетесь провернуть некоторые классы, вам нужно либо сидеть и тщательно думать о том, как инкапсулировать каждый член, либо просто выставлять его как свойство, и беспокоиться о том, как он инициализируется позже. Просто кажется, что самый простой подход в эти дни-выставлять вещи как свойства и не быть таким осторожным. Может быть, я просто ошибаюсь, но это просто мой опыт, espeically с новым языком C# особенности.

3 ответов


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

забрать объект реляционной базы; большинство из них позволят вам укажите, как они собираются гидратировать объекты; например, NHibernate позволяет вам так сказать access="property" или access="field.camelcase" и подобные. Это позволяет инкапсулировать свойства.

инъекции зависимостей работает на других типах, которые у вас есть, в основном те, которые не являются сущностями, хотя вы можете объединить AOP+ORM+IOC в некоторых очень хороших способах улучшить состояние этих вещей. IoC часто используется из слоев над объектами домена, если вы создаете управляемый данными приложение, которым, я думаю, вы являетесь, так как вы говорите об Ормах.

Они ("они" являются приложениями и доменными службами и другими внутренними классами программы) раскрывают свои зависимости, но на самом деле могут быть инкапсулированы и протестированы в еще лучшей изоляции, чем раньше, поскольку парадигмы проектирования по контракту / дизайна по интерфейсу, которые вы часто используете при издевательстве над зависимостями в тестировании на основе макетов( в сочетании с IoC), переместят вас к классу как компоненту "семантика." Я имею в виду: каждый класс, построенный с использованием вышеизложенного, будет лучше инкапсулирован.

обновлено для urig: это справедливо для обоих подвергая бетона зависимости и выставление интерфейсов. Сначала об интерфейсах: я намекал выше, что службы и другие классы приложений, которые имеют зависимости, могут с ООП зависеть от контрактов/интерфейсов, а не от конкретных реализаций. В C / C++ и более старых языках не было интерфейс и абстрактные классы могут зайти только так далеко. Интерфейсы позволяют привязывать различные экземпляры среды выполнения к одному интерфейсу, не беспокоясь о утечке внутреннего состояния, от которого вы пытаетесь уйти при абстрагировании и инкапсуляции. С абстрактными классами вы все равно можете предоставить реализацию класса, просто вы не можете создать ее экземпляр, но наследникам все равно нужно знать об инвариантах в вашей реализации, и это может испортить государство.

во-вторых, о конкретных классах как свойствах: вы должны быть осторожны с тем, какие типы типов ;) вы выставляете как свойства. Скажем, у вас есть список в вашем экземпляре; затем не выставляйте IList как свойство; это, вероятно, будет протекать, и вы не можете гарантировать, что потребители интерфейса не добавляют вещи или не удаляют вещи, от которых вы зависите; вместо этого выставляйте что-то вроде IEnumerable и возвращайте копию списка или даже лучше сделайте это как метод: общественного IEnumerable MyCollection { get { return _List.Enum ();}} и вы можете быть уверены на 100%, чтобы получить как производительность, так и инкапсуляцию. Никто не может добавить или удалить этот IEnumerable, и вам все равно не нужно выполнять дорогостоящую копию массива. Соответствующий вспомогательный метод:

static class Ext {
    public static IEnumerable<T> Enum<T>(this IEnumerable<T> inner) {
        foreach (var item in inner) yield return item;
    }
}

поэтому, пока вы не можете получить 100% инкапсуляцию, скажем, создавая перегруженные операторы/метод equals, вы можете приблизиться к своим общедоступным интерфейсам.

вы также можете использовать новые функции .Net 4.0, построенные на Spec# для проверки контрактов, о которых я говорил выше.

сериализация всегда будет там и был в течение длительного времени. Раньше, до интернет-области он использовался для сохранения графа объекта на диск для последующего извлечения, теперь он используется в веб-службах, в семантике копирования и при передаче данных, например, в браузер. Это не обязательно нарушает инкапсуляцию, если вы поместите несколько [Несериализованных] атрибутов или эквивалентов на правильный поля.

инициализаторы объектов не то же самое, что конструкторы, они просто способ сворачивания нескольких строк кода. Значения / экземпляры в {} не будут назначены до тех пор, пока не будут запущены все ваши конструкторы, поэтому в принципе это то же самое, что не использовать инициализаторы объектов.

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


Private-члены по-прежнему невероятно важно. Управление доступом к внутренним объектным данным всегда хорошо и не должно игнорироваться.

много раз частные методы, которые я нашел, чтобы быть излишним. В большинстве случаев, если работа, которую вы делаете, достаточно важна, чтобы вырваться, вы можете рефакторировать ее таким образом, что либо a) частный метод тривиален, либо b) является неотъемлемой частью других функций.

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

Я не совсем скидка частные методы. Каждый раз, когда есть важные внутренние алгоритмы, которые действительно не имеют смысла вне класса, нет причин раскрывать эти методы.


Я думаю, что инкапсуляция по-прежнему важна, она помогает больше в библиотеках, чем что-либо imho. Вы можете создать библиотеку, которая делает X, но вам не нужно, чтобы все знали, как Х был создан. И если вы хотите создать его более конкретно, чтобы запутать способ создания X. То, как я узнал о инкапсуляции, я помню также, что вы всегда должны определять свои переменные как частные, чтобы защитить их от атаки данных. Для защиты от взлома хакером вашего кода и доступа переменные, которые они не должны использовать.