2009-02-10 2 views
6

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

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

п = п + 1

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

addOne(n) 
    n = n + 1 
    return n 

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

ответ

12

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

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

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

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

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

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

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

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

+0

Если функция была вызвана «AddX», где X - это значение, которое в настоящее время имеет значение 1, но это может измениться, тогда будет скрыта информация. Добавленное конкретное значение будет скрыто в определении функции. –

+0

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

+0

@Serx Функция addOne выражает «что» это делает и скрывает «как» она это делает. Таким образом, это будет квалифицироваться как абстракция + скрытие информации. Почему вы так сосредоточены на объектах? И, как утверждает Эрвиккер, функции могут разделить ответственность, но я нахожу это довольно расплывчатым критерием для инкапсуляции. – eljenso

1

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

Проверить this из:

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

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

-1

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

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

+0

хаос Почему это так? что здесь не так? –

+0

@Foysal: Я понятия не имею. – chaos

1

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

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

[EDIT] Что касается примера в обновленном вопросе: это зависит от того, насколько узким или широким вы определяете инкапсуляцию. Ваш пример AddOne не скрывает ничего, что я считаю. Это будет скрытие/инкапсуляция информации, если ваша переменная будет индексом массива, и вы вызовете свой метод moveNext и, возможно, имеете еще одну функцию setValue и getValue. Это позволило бы людям (вместе, возможно, с некоторыми другими функциями) перемещаться по вашей структуре и настройкам и получать переменные, чтобы они знали о вас, используя массив.Если язык программирования будет поддерживать другие или более богатые концепции, вы можете изменить реализацию moveNext, setValue и getValue с изменением значения и интерфейса. Для меня это инкапсуляция.

+0

Я обновил свой вопрос, чтобы уточнить двусмысленность (надеюсь). – 2009-02-10 20:12:10

15

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

См. Также Meyers.

+0

Я бы предположил, что ответ явно да.При вызове функции вы не должны беспокоиться о том, как функция выполняет задание. –

+0

Согласовано. Для меня это тоже очевидно. Некоторые люди просто зависают над тем, что говорит их учебник ООП. Возвращаясь к определению, эта инкапсуляция скрывает детали реализации, поэтому пользователю не нужно беспокоиться о них, функция, очевидно, является примером инкапсуляции. – jalf

+0

Справа. Дело в том, можете ли вы изменить реализацию без влияния на код с помощью реализации? Если это так, это поведение (что делает этот код) инкапсулировано. – alphadogg

3

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

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

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

+0

Получил. Пример передачи очень прост. Спасибо. – 2009-02-10 20:16:02

3

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

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

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

Update: чтобы ответить на ваш обновленный вопрос, как «ввод кода в методе» и «с использованием модификатора доступа» различные способы инкапсулирования логики, но каждый один действует на другом уровне.

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

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

+0

Вы также не можете сказать, что ваш код хорошо разработан, потому что вы разделили его на классы: –

+0

@Mo о, конечно. Я хочу сказать, что если вы собираете инкапсуляцию вдоль одной оси или на одном уровне абстракции, а затем говорите «хорошо, предположим, что моя программа инкапсулирована», вы делаете это неправильно. –

5

Уверенный, что это.

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

Инкапсуляция была задолго до объектно-ориентированного программирования :)

1

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

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

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

+0

Мне нравится ваш пример двигателя, но вы потеряли меня с помощью своего юмористического примера с искусственной рукой. –

0

Эталонная модель Open Distributed Processing - написанный Международной организации по стандартизации - определяет следующие понятия:

Entity: Любые конкретные или абстрактный предмет интереса.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Все большое удовольствие.

0

В строгой объектно-ориентированной терминологии может возникнуть соблазн сказать «нет», «простая» функция недостаточно мощна, чтобы называться инкапсуляцией ... но in the real world очевидным ответом является «да, функция инкапсулирует некоторый код», ,

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

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

1

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

Смежные вопросы