Единый принцип доступа - Uniform access principle

В принцип единого доступа из компьютерное программирование был выдвинут Бертран Мейер (первоначально в Построение объектно-ориентированного программного обеспечения ). В нем говорится: «Все услуги, предлагаемые модуль должны быть доступны через единообразную нотацию, которая не показывает, реализуются ли они посредством хранения или посредством вычислений ".[1] Этот принцип обычно применяется к синтаксис из объектно-ориентированный языки программирования. В более простой форме он гласит, что не должно быть синтаксической разницы между работой с атрибут, предварительно вычисленный свойство, или же метод /запрос объекта.

В то время как большинство примеров сосредоточено на аспекте «чтения» принципа (т.е. получении значения), Мейер показывает, что последствия «записи» (т.е. изменение значения) принципа труднее описать в его ежемесячной колонке на Язык программирования Eiffel Официальный веб-сайт.[2]

Объяснение

Проблема, которую решает Мейер, связана с обслуживанием больших программных проектов или программных библиотек. Иногда при разработке или сопровождении программного обеспечения необходимо после того, как большой объем кода был на месте, изменить класс или объект таким образом, чтобы преобразовать то, что было просто доступом к атрибуту, в вызов метода. Языки программирования часто используют другой синтаксис для доступа к атрибутам и вызова метода (например, object.something против object.something ()). В популярных языках программирования того времени изменение синтаксиса потребовало бы изменения исходного кода во всех местах, где использовался атрибут. Это может потребовать изменения исходного кода во многих разных местах в очень большом объеме исходного кода. Или, что еще хуже, если изменение находится в библиотеке объектов, используемой сотнями клиентов, каждому из этих клиентов придется найти и изменить все места, где атрибут использовался в их собственном коде, и перекомпилировать свои программы.

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

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

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

Пример UAP

Если в языке используется синтаксис вызова метода, он может выглядеть примерно так.

// Предположим, что print отображает переданную ему переменную, с скобками или без них // Установите для атрибута Foo 'bar' значение 5.Foo.bar (5) print Foo.bar ()

При выполнении должно отображаться:

5

Так или иначе Foo.bar (5) вызывает функцию или просто устанавливает атрибут, скрытый от вызывающего. Foo.bar () просто извлекает значение атрибута или вызывает функцию для вычисления возвращенного значения, это деталь реализации, скрытая от вызывающего.

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

Foo.bar = 5 распечатать Foo.bar

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

Проблемы

Однако сам UAP может привести к проблемам, если его использовать в местах, где существуют различия между методами доступа. нет незначительно, например, когда возвращаемое значение является дорогостоящим для вычисления или запускает операции кеширования.[1]

Примеры языков

Рубин

Рассмотрим следующие

у = Яйцо.новый("Зеленый")у.цвет = "Белый" ставит у.цвет

Теперь класс Egg можно определить следующим образом

учебный класс Яйцо  attr_accessor :цвет  def инициализировать(цвет)    @цвет = цвет  конецконец

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

учебный класс Яйцо    def инициализировать(цвет)    @rgb_color = to_rgb(цвет)  конец  def цвет     to_color_name(@rgb_color)  конец   def цвет=(цвет)      @rgb_color = to_rgb(цвет)  конец  частный  def to_rgb(color_name)     .....  конец  def to_color_name(цвет)     ....  конецконец

Обратите внимание, как хотя цвет выглядит как атрибут в одном случае и как пара методов в следующем, интерфейс класса остается тем же. Человек, обслуживающий класс Egg, может переключаться с одной формы на другую, не опасаясь взлома кода вызывающего абонента. Руби следует пересмотренному UAP, attr_accessor: цвет действует только как синтаксический сахар для создания методов доступа / установки для цвет. В Ruby нет способа получить переменную экземпляра из объекта, не вызывая для него метод.

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

Python

Свойства Python может использоваться, чтобы разрешить вызов метода с тем же синтаксисом, что и доступ к атрибуту. В то время как UAP Мейера будет иметь единую нотацию как для доступа к атрибутам, так и для вызова метода (синтаксис вызова метода), язык с поддержкой свойств по-прежнему поддерживает отдельные обозначения для доступа к атрибутам и методам. Свойства позволяют использовать обозначение атрибута, но для того, чтобы скрыть факт вызова метода вместо простого извлечения или установки значения.

Таким образом, Python оставляет возможность присоединения к UAP на усмотрение отдельного программиста. Встроенный @свойство функция предоставляет простой способ украсить любой заданный метод в синтаксисе доступа к атрибутам, тем самым абстрагируясь от синтаксической разницы между вызовами методов и доступом к атрибутам.[3]

В Python у нас может быть код, который обращается к Яйцо объект, который можно определить так, чтобы вес и цвет были простыми атрибутами, как показано ниже

'''>>> egg = Egg (4.0, «белый»)>>> egg.color = "зеленый">>> печать (яйцо)Яйцо (4.0, зеленое)'''учебный класс Яйцо:    def __в этом__(себя, масса, цвет) -> Никто:        себя.масса = масса        себя.цвет = цвет    def __str__(себя) -> ул.:        возвращаться ж'{__class __.__ name__}({собственный вес}, {self.color})'

Или объект Egg может использовать свойства и вместо этого вызывать методы получения и установки

# ... (отрывок) ...учебный класс Яйцо:    def __в этом__(себя, weight_oz: плавать, color_name: плавать) -> Никто:        себя.масса = weight_oz        себя.цвет = color_name            @свойство    def цвет(себя) -> ул.:        '' Цвет яйца ''        возвращаться to_color_str(себя._color_rgb)    @цвет.сеттер    def цвет(себя, color_name: ул.) -> Никто:        себя._color_rgb = to_rgb(color_name)       @свойство    def масса(себя) -> плавать:        '' Вес в унциях ''        возвращаться себя._weight_gram / 29.3    @масса.сеттер    def масса(себя, weight_oz: плавать) -> Никто:        себя._weight_gram = 29.3 * weight_oz    # ... (отрывок) ...
Коды сниппета следующие:
импорт webcolors# класс Яйцо:def to_color_str(rgb: webcolors.IntegerRGB) -> ул.:    пытаться:        возвращаться webcolors.rgb_to_name(rgb)    Кроме ValueError:        возвращаться webcolors.rgb_to_hex(rgb)    def to_rgb(color_name: ул.) -> webcolors.IntegerRGB:    пытаться:        возвращаться webcolors.name_to_rgb(color_name)    Кроме ValueError:        возвращаться webcolors.hex_to_rgb(color_name)если __имя__ == "__главный__":    импорт доктест    доктест.testmod()

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

C #

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

общественный учебный класс Фу{    частный нить _имя;    // Свойство    общественный int Размер    {        получать;    // Геттер        набор;    // Сеттер    }    // Свойство    общественный нить Имя    {        получать { возвращаться _имя; }     // Геттер        набор { _имя = ценить; }    // Сеттер    }}

В приведенном выше примере класс Фу содержит два свойства, Размер и Имя. В Размер свойство - это целое число, которое можно читать (получать) и записывать (устанавливать). Точно так же Имя свойство - это строка, которую также можно читать и изменять, но ее значение хранится в отдельной (частной) переменной класса _имя.

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

Использование свойств использует UAP, как показано в приведенном ниже коде.

    общественный Фу CreateFoo(int размер, нить имя)    {        вар фу = новый Фу();        фу.Размер = размер; // Установщик свойств        фу.Имя = имя; // Установщик свойств        возвращаться фу;    }

C ++

C ++ не имеет ни UAP, ни свойств, когда объект изменяется так, что атрибут (цвет) становится парой функций (getA, setA). В любом месте, где используется экземпляр объекта и устанавливается или получает значение атрибута (x = obj.color или же obj.color = x) необходимо изменить, чтобы вызвать одну из функций. (x = obj.getColor () или же obj.setColor (x)). Используя шаблоны и перегрузку операторов, можно подделать свойства, но это сложнее, чем в языках, которые напрямую поддерживают свойства. Это усложняет обслуживание программ на C ++. Распределенные библиотеки объектов C ++ должны внимательно относиться к тому, как они предоставляют доступ к данным членов.

JavaScript

JavaScript поддерживает вычисляемые свойства с 2009 года.[4]

Оболочка нового поколения

В оболочке следующего поколения доступ к полям объектов осуществляется через . синтаксис, аналогичный другим языкам программирования. В соответствии с остальным языком, синтаксис является сокращением для вызова метода. myobj.myfield интерпретируется как вызов метода . с аргументами myobj и Myfield.

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

По аналогии, .= метод называется myobj.myfield = myval синтаксис.

В следующем примере демонстрируется поведение по умолчанию . и .= методы.

тип EggF init(e: Яйцо, цвет: Str) {e.color = цвет}е = Яйцо("Зеленый")e.color = "Белый"эхо(e.color)

В следующем примере демонстрируется настраиваемое поведение . и .= методы. Код реализует аксессор для цвет поле.

тип EggF init(e: Яйцо, цвет: Str) {e.rgb_color = RGBColor(цвет)}F.(e: Яйцо, поле: Str) {сторожить поле == 'цвет'e.rgb_color.name()}F.=(e: Яйцо, поле: Str, значение: Str) {сторожить поле == 'цвет'e.rgb_color = RGBColor(ценить)}е = Яйцо("Зеленый")e.color = "Белый"эхо(e.color)

Рекомендации

  1. ^ а б «Принцип единого доступа». c2 вики. Получено 6 августа 2013.
  2. ^ Мейер, Бертран. «Колонка EiffelWorld: Бизнес плюс удовольствие». Получено 6 августа 2013.
  3. ^ Официальные документы Python, встроенные функции
  4. ^ w3schools.com, Аксессоры Javascript