7

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

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

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

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

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

+0

Не позволяйте никому обмануть вас, эти инициализаторы объектов - это просто синтаксический сахар. – BobbyShaftoe

+0

Я знаю, что инициализаторы объектов не являются конструкторами, но это не моя точка. Они позволяют создавать объект с использованием конструктора по умолчанию и задавать свойства вручную. –

+0

Не совсем уверен, какая разница между ними и просто печатать каждое утверждение, чтобы установить каждое свойство. Но хорошо. – BobbyShaftoe

ответ

3

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

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

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

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

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

Во-вторых, о конкретных классах как свойствах: вы должны быть осторожны относительно того, какие типы типов;) вы раскрываете как свойства. Скажем, у вас есть список в вашем экземпляре; то не раскрывайте IList как свойство; это, вероятно, будет протекать, и вы не можете гарантировать, что потребители интерфейса не добавляют вещи или не удаляют вещи, от которых вы зависите; вместо этого выведите что-то вроде IEnumerable и верните копию списка или, что еще лучше, сделайте это как метод: public 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% герметизации, скажем, создающие перегружено равно операторы/метод, который вы можете получить рядом с вашими открытыми интерфейсами.

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

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

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

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

+0

Hi Henrik, Можете ли вы привести пример того, как «они раскрывают свои зависимости, но на самом деле могут быть инкапсулированы и протестированы в еще лучшей изоляции, чем раньше»? Если класс предоставляет свои зависимости в публичных свойствах, как это может не нарушить инкапсуляцию? Спасибо! urig – urig

+0

Возвращаясь, глядя на это, я вижу, что я был нечетким в том, что я имел в виду. – Henrik

0

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

1

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

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

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

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