Вопрос

Помещая функциональность в функцию, является ли это само по себе примером инкапсуляции или вам нужно использовать объекты для обеспечения инкапсуляции?

Я пытаюсь понять концепцию инкапсуляции.То, что я подумал, было, если я уйду от чего-то подобного этому:

n = n + 1

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

addOne(n)
    n = n + 1
    return n

Или это скорее тот случай, когда это только инкапсуляция, если я скрываю детали addOne от внешнего мира - например, если это объектный метод, и я использую модификатор доступа private / protected?

Это было полезно?

Решение

Возможно, вы путаете абстракцию с инкапсуляцией, которая понимается в более широком контексте объектной ориентации.

Инкапсуляция должным образом включает в себя все три из следующих:

  • Абстракция
  • Скрытие реализации
  • Разделение ответственности

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

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

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

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

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

Другие советы

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

Видишь Мейерс слишком.

Конечно, это так.

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

Инкапсуляция появилась задолго до ООП :)

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

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

Возвращаясь к примеру с автомобилем:Автоматическая коробка передач обеспечивает хорошую герметизацию:Как водитель, вы заботитесь о движении вперед / назад и скорости.Механическая коробка передач - это плохая герметизация:С точки зрения водителя, конкретная передача, необходимая для низких / высоких скоростей, как правило, не имеет отношения к намерениям водителя.

Нет, объекты не являются требуемый для инкапсуляции.В самом широком смысле "инкапсуляция" просто означает "скрытие деталей от посторонних глаз", и в этом отношении метод инкапсулирует детали своей реализации.

Однако это на самом деле не означает, что вы можете выйти и сказать, что ваш код хорошо разработан только потому, что вы разделили его на методы.Программа, состоящая из 500 общедоступных методов, не намного лучше, чем та же программа, реализованная в одном методе из 1000 строк.

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

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

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

Помечение метода в классе как (скажем) "private" скрывает этот метод, так что потребитель класс не нужно беспокоиться об этом;они беспокоятся только об открытых методах (или свойствах) вашего класса.

Это вещь на уровне компонентов

Проверить это вон:

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

(Я не совсем понимаю ваш вопрос, дайте мне знать, если эта ссылка не удовлетворит ваши сомнения)

Абстрактная концепция инкапсуляции означает, что вы скрываете детали реализации.Объектная ориентация - это лишь один из примеров использования ecnapsulation.Другим примером является язык под названием module-2, который использует (или использованные) модули реализации и модули определения.Модули определения скрывали фактическую реализацию и, следовательно, обеспечивали инкапсуляцию.

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

[РЕДАКТИРОВАТЬ] Что касается примера в обновленном вопросе:это зависит от того, насколько узко или широко вы определяете инкапсуляцию.Я полагаю, что ваш пример addOne ничего не скрывает.Это было бы сокрытием / инкапсуляцией информации, если бы ваша переменная была индексом массива, и вы бы вызвали свой метод MoveNext и, возможно, имели другую функцию setValue и GetValue .Это позволило бы людям (возможно, вместе с некоторыми другими функциями) ориентироваться в вашей структуре и настройках и получать переменные, зная, что вы используете массив.Если бы ваш язык программирования поддерживал другие или более богатые концепции, вы могли бы изменить реализацию MoveNext, setValue и GetValue, изменив значение и интерфейс.Для меня это и есть инкапсуляция.

Давайте несколько упростим это с помощью аналогии:вы поворачиваете ключ в своей машине, и она заводится.Вы знаете, что это нечто большее, чем просто ключ, но вы не иметь знать что что там происходит внутри.Для вас поворот ключа = запуск двигателя.Интерфейс ключа (то есть, например, вызов функции) скрывает реализацию стартера, вращающего двигатель, и т.д...(реализация). Это инкапсуляция.Вы избавлены от необходимости знать, что происходит под капотом, и вы счастливы этому.

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

Для инкапсуляции можно использовать любой метод, который вы можете придумать, чтобы скрыть информацию (классы, функции, динамические библиотеки, макросы).

Инкапсуляция - это процесс, в котором атрибуты (элемент данных) и поведение (функция-член) объектов, объединенных в единую сущность, называются классом.

Эталонная модель открытой распределенной обработки , написанная Международной организацией по стандартизации , определяет следующие концепции:

Сущность:Любая конкретная или абстрактная вещь, представляющая интерес.

Объект:Модель объекта.Объект характеризуется своим поведением и, следовательно, его состоянием.

Поведение (объекта):Набор действий с набором ограничений на то, когда они могут произойти.

Интерфейс:Абстракция поведения объекта, которая состоит из подмножества взаимодействий этого объекта вместе с набором ограничений на то, когда они могут произойти.

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

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

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

Есть ли у функции поведение?Это, безусловно, так и есть:он содержит набор действий (которые могут представлять собой любое количество исполняемых инструкций), которые выполняются в соответствии с ограничением, согласно которому функция должна быть вызвана откуда-либо, прежде чем она сможет выполняться.Функция не может быть вызвана спонтанно в любое время без причинного фактора.Звучит по-юридическому?Еще бы.Но, тем не менее, давайте работать дальше.

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

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

Поскольку некоторая информация доступна через его интерфейс, некоторая информация должна быть скрыта и недоступна внутри функции.(Свойство, которое проявляет такая информация, называется сокрытием информации, которое Парнас определил, утверждая, что модули должны быть спроектированы так, чтобы скрывать как сложные решения, так и решения, которые могут измениться.) Итак, какая информация скрыта внутри функции?

Чтобы убедиться в этом, мы должны сначала рассмотреть масштаб.Легко утверждать, что, например, классы Java могут быть инкапсулированы в пакет:некоторые классы будут общедоступными (и, следовательно, будут интерфейсом пакета), а некоторые будут закрытыми для пакета (и, следовательно, информация будет скрыта внутри пакета).В теории инкапсуляции классы образуют узлы, а пакеты - инкапсулированные области, причем все вместе образует инкапсулированный граф;граф классов и пакетов называется третьим графом.

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

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

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

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

Принимая эти (возможно, слегка шаткие) определения, тогда мы можем сказать "да":помещение функциональности в функцию действительно относится к инкапсуляции.Инкапсуляция блоков внутри функций - это первый график.

Однако есть одно предостережение.Считаете ли вы, что пакет, каждый класс которого был общедоступным, должен быть инкапсулирован?Согласно приведенным выше определениям, он действительно проходит тест, поскольку вы можете сказать, что интерфейс к пакету (т. е. все общедоступные классы) действительно предлагает подмножество поведения пакета для других пакетов.Но подмножеством в данном случае является поведение всего пакета, поскольку никакие классы не являются скрытыми от информации.Таким образом, несмотря на полное соответствие приведенным выше определениям, мы чувствуем, что это не соответствует духу определений, поскольку, несомненно, что-то должно быть скрыто с информацией, чтобы можно было претендовать на истинную инкапсуляцию.

То же самое верно и для примера, который вы приводите.Мы, конечно, можем рассматривать n = n + 1 как единый блок Маккабиана, поскольку он (и оператор return) представляют собой единый последовательный поток выполнений.Но функция, в которую вы помещаете это, таким образом, содержит только один блок, и этот блок является единственным общедоступным блоком функции, и, следовательно, в предлагаемой вами функции нет блоков, скрытых от информации.Таким образом, это может удовлетворять определению инкапсуляции, но я бы сказал, что это не удовлетворяет духу.

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

Есть две силы , которые мотивируют инкапсуляцию:семантическое и логическое.

Семантическая инкапсуляция просто означает инкапсуляцию, основанную на значении инкапсулируемых узлов (если использовать общий термин).Итак, если я скажу вам, что у меня есть два пакета, один называется "animal", а другой - "mineral", а затем приведу вам три класса Dog, Cat и Goat и спрошу, в какие пакеты следует инкапсулировать эти классы, то, не имея никакой другой информации, вы будете совершенно правы, утверждая, что семантика системы предполагает, что три класса должны быть инкапсулированы в пакет "animal", а не в "mineral".

Однако другой мотивацией для инкапсуляции является логика.

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

Логически инкапсулировать систему означает идентифицировать некоторое математическое свойство системы, которое зависит от ее конфигурации, а затем сконфигурировать эту систему таким образом, чтобы это свойство было математически минимизировано.

Теория инкапсуляции предполагает, что все инкапсулированные графы выражают максимальное потенциальное число ребер (MPE).Например, в системе классов и пакетов Java MPE - это максимальное потенциальное количество зависимостей исходного кода, которые могут существовать между всеми классами этой системы.Два класса в одном пакете не могут быть скрыты друг от друга с точки зрения информации, и поэтому оба потенциально могут создавать зависимости друг от друга.Однако два закрытых класса в отдельных пакетах могут не формировать зависимости друг от друга.

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

Учитывая n классов и требование наличия p открытых классов на пакет, теория инкапсуляции показывает, что количество пакетов, r, которое мы должны иметь для минимизации MPE, задается уравнением:r = sqrt(n/p).

Это также относится к количеству функций, которыми вы должны обладать, учитывая общее количество n блоков McCabian в вашей системе.Функции всегда имеют только один общедоступный блок, как мы упоминали выше, и поэтому уравнение для количества функций r, которые должны быть в вашей системе, упрощается до:r = sqrt(n).

По общему признанию, мало кто учитывал общее количество блоков в своей системе при практике инкапсуляции, но это легко делается на уровне класса / пакета.И, кроме того, минимизация MPE почти полностью интуитивна:это делается путем минимизации количества общедоступных классов и попытки равномерно распределить классы по пакетам (или, по крайней мере, избегать наличия большинства пакетов, скажем, с 30 классами, и одного огромного пакета с 500 классами, и в этом случае внутренний MPE последнего может легко подавить MPE всех остальных).

Таким образом, инкапсуляция предполагает достижение баланса между семантическим и логическим.

Все это очень весело.

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

для пуристов OO, которые возмущаются этим богохульством, рассмотрим статический анонимный класс без состояния и с единственным методом;если функция addOne() не является инкапсуляционной, то и этот класс не является таковым!

и просто чтобы быть педантичным, инкапсуляция - это форма абстракции, а не наоборот.;-)

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

Так что нет, вы, скорее всего, не используете инкапсуляцию только потому, что вы помещаете метод в класс, а не используете его как глобальную функцию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top