HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Абстракция позволяет нам не думать о деталях реализации и сосредоточиться на её использовании. Более того, при необходимости реализацию абстракции можно всегда переписать, не боясь сломать использующий её код. Но есть ещё одна важная причина, по которой нужно использовать абстракцию - соблюдение инвариантов.</p>
1 <p>Абстракция позволяет нам не думать о деталях реализации и сосредоточиться на её использовании. Более того, при необходимости реализацию абстракции можно всегда переписать, не боясь сломать использующий её код. Но есть ещё одна важная причина, по которой нужно использовать абстракцию - соблюдение инвариантов.</p>
2 <p>Инвариант в программировании - логическое выражение, определяющее непротиворечивость состояния (набора данных).</p>
2 <p>Инвариант в программировании - логическое выражение, определяющее непротиворечивость состояния (набора данных).</p>
3 <p>Разберёмся на примере. Когда мы описали конструктор и селекторы для рациональных чисел, то неявно подразумевали выполнение следующих инвариантов:</p>
3 <p>Разберёмся на примере. Когда мы описали конструктор и селекторы для рациональных чисел, то неявно подразумевали выполнение следующих инвариантов:</p>
4 <p>Передав в конструктор рационального числа числитель и знаменатель, мы ожидаем, что получим их (те же числа), если применим селекторы к этому рациональному числу. Именно так определяется корректность работы данной абстракции. Этот код практически является тестами!</p>
4 <p>Передав в конструктор рационального числа числитель и знаменатель, мы ожидаем, что получим их (те же числа), если применим селекторы к этому рациональному числу. Именно так определяется корректность работы данной абстракции. Этот код практически является тестами!</p>
5 <p>Инварианты существуют относительно любой операции. И иногда они довольно хитрые. Например, рациональные числа можно сравнивать между собой, но не прямым способом, потому, что одни и те же дроби можно представлять разными способами:<em>1/2</em>и<em>2/4</em>. Код, который не учитывает этого факта, работает некорректно:</p>
5 <p>Инварианты существуют относительно любой операции. И иногда они довольно хитрые. Например, рациональные числа можно сравнивать между собой, но не прямым способом, потому, что одни и те же дроби можно представлять разными способами:<em>1/2</em>и<em>2/4</em>. Код, который не учитывает этого факта, работает некорректно:</p>
6 <p>Задача приведения дроби к нормальной форме называется<em>нормализацией</em>. В это понятие входит несколько операций, например, сокращение дроби, определение знака, перенос знака в числитель. Реализовать нормализацию можно разными способами. Самый очевидный - выполнять её во время создания дроби, внутри функции makeRational(). Другой - выполнять нормализацию уже при обращении через функции getNumer() и getDenom(). Последний способ обладает недостатком - вычисление нормальной формы происходит на каждый вызов. Избежать этого можно, используя технику<a>мемоизации</a>.</p>
6 <p>Задача приведения дроби к нормальной форме называется<em>нормализацией</em>. В это понятие входит несколько операций, например, сокращение дроби, определение знака, перенос знака в числитель. Реализовать нормализацию можно разными способами. Самый очевидный - выполнять её во время создания дроби, внутри функции makeRational(). Другой - выполнять нормализацию уже при обращении через функции getNumer() и getDenom(). Последний способ обладает недостатком - вычисление нормальной формы происходит на каждый вызов. Избежать этого можно, используя технику<a>мемоизации</a>.</p>
7 <p>Учитывая новые вводные, становится понятно, что инвариант, связывающий конструктор и селекторы, нуждается в модификации. Функции getNumer() и getDenom() должны вернуть не переданные значения, а значения после нормализации (если дробь уже нормализована, то это будут те же самые значения).</p>
7 <p>Учитывая новые вводные, становится понятно, что инвариант, связывающий конструктор и селекторы, нуждается в модификации. Функции getNumer() и getDenom() должны вернуть не переданные значения, а значения после нормализации (если дробь уже нормализована, то это будут те же самые значения).</p>
8 <p>Абстракция не только прячет от нас реализацию, но и отвечает за соблюдение инвариантов. Любая работа в обход абстракции чревата тем, что не будут учтены внутренние преобразования:</p>
8 <p>Абстракция не только прячет от нас реализацию, но и отвечает за соблюдение инвариантов. Любая работа в обход абстракции чревата тем, что не будут учтены внутренние преобразования:</p>
9 <p>То есть работа с данными напрямую, минуя абстракцию, может легко сломать инварианты, которые обеспечивались дополнительной логикой в конструкторе или селекторах. Поэтому важно пользоваться кодом так, как было задумано авторами.</p>
9 <p>То есть работа с данными напрямую, минуя абстракцию, может легко сломать инварианты, которые обеспечивались дополнительной логикой в конструкторе или селекторах. Поэтому важно пользоваться кодом так, как было задумано авторами.</p>
10 <p>Глядя на примеры выше, возникает закономерный вопрос. А можно ли сделать так, чтобы обойти абстракцию было нельзя? Глобально - да. Такой подход называют сокрытием данных (data hiding). Обычно для обеспечения сокрытия в языках используются специальный синтаксис. В PHP за это отвечают модификаторы доступа к свойствам объектов public, protected и private, с которыми мы скоро познакомимся. Однако, защиту данных можно организовать и без специальных средств, только за счёт функций высшего порядка. Данный способ основан на создании абстракций с помощью анонимных функций, замыканий и передачи сообщений (подробнее в SICP). Если вы хотите узнать об этом больше, то попробуйте наш курс<a>PHP: Составные данные</a></p>
10 <p>Глядя на примеры выше, возникает закономерный вопрос. А можно ли сделать так, чтобы обойти абстракцию было нельзя? Глобально - да. Такой подход называют сокрытием данных (data hiding). Обычно для обеспечения сокрытия в языках используются специальный синтаксис. В PHP за это отвечают модификаторы доступа к свойствам объектов public, protected и private, с которыми мы скоро познакомимся. Однако, защиту данных можно организовать и без специальных средств, только за счёт функций высшего порядка. Данный способ основан на создании абстракций с помощью анонимных функций, замыканий и передачи сообщений (подробнее в SICP). Если вы хотите узнать об этом больше, то попробуйте наш курс<a>PHP: Составные данные</a></p>