Является ли функция примером инкапсуляции? - PullRequest
6 голосов
/ 10 февраля 2009

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

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

n = n + 1

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

addOne(n)
    n = n + 1
    return n

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

Ответы [ 12 ]

0 голосов
/ 11 февраля 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

0 голосов
/ 10 февраля 2009

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

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

...