HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Абстракция позволяет нам не думать о деталях реализации и сосредоточиться на ее использовании. Также при необходимости реализацию абстракции можно всегда переписать и не бояться сломать использующий ее код. Но есть еще одна важная причина, по которой нужно использовать абстракцию - соблюдение инвариантов.</p>
1 <p>Абстракция позволяет нам не думать о деталях реализации и сосредоточиться на ее использовании. Также при необходимости реализацию абстракции можно всегда переписать и не бояться сломать использующий ее код. Но есть еще одна важная причина, по которой нужно использовать абстракцию - соблюдение инвариантов.</p>
2 <p>В этом уроке мы познакомимся с инвариантами, нормализацией и понятием data hiding.</p>
2 <p>В этом уроке мы познакомимся с инвариантами, нормализацией и понятием data hiding.</p>
3 <h2>Инварианты</h2>
3 <h2>Инварианты</h2>
4 <p><strong>Инвариант в программировании</strong>- логическое выражение, которое определяет непротиворечивость состояния - набора данных.</p>
4 <p><strong>Инвариант в программировании</strong>- логическое выражение, которое определяет непротиворечивость состояния - набора данных.</p>
5 <p>Разберемся на примере. Когда мы описали конструктор и селекторы для рациональных чисел, то неявно подразумевали выполнение следующих инвариантов:</p>
5 <p>Разберемся на примере. Когда мы описали конструктор и селекторы для рациональных чисел, то неявно подразумевали выполнение следующих инвариантов:</p>
6 <p>Когда мы передаем в конструктор рационального числа числитель и знаменатель, то ожидаем, что получим те же числа, если применим селекторы к этому рациональному числу. Так определяется корректность работы данной абстракции. Этот код практически является тестами.</p>
6 <p>Когда мы передаем в конструктор рационального числа числитель и знаменатель, то ожидаем, что получим те же числа, если применим селекторы к этому рациональному числу. Так определяется корректность работы данной абстракции. Этот код практически является тестами.</p>
7 <p>Инварианты существуют относительно любой операции. И иногда они довольно хитрые. Например, рациональные числа можно сравнивать между собой, но не прямым способом. Одни и те же дроби можно представлять разными способами:<em>1/2</em>и<em>2/4</em>. Код, который не учитывает этого факта, работает некорректно:</p>
7 <p>Инварианты существуют относительно любой операции. И иногда они довольно хитрые. Например, рациональные числа можно сравнивать между собой, но не прямым способом. Одни и те же дроби можно представлять разными способами:<em>1/2</em>и<em>2/4</em>. Код, который не учитывает этого факта, работает некорректно:</p>
8 <h2>Нормализация</h2>
8 <h2>Нормализация</h2>
9 <p>Задача приведения дроби к нормальной форме называется<strong>нормализацией</strong>. Реализовать ее можно разными способами. Самый очевидный - выполнять нормализацию во время создания дроби, внутри функции make_rational. Другой - выполнять нормализацию уже при обращении через функции get_numer и get_denom.</p>
9 <p>Задача приведения дроби к нормальной форме называется<strong>нормализацией</strong>. Реализовать ее можно разными способами. Самый очевидный - выполнять нормализацию во время создания дроби, внутри функции make_rational. Другой - выполнять нормализацию уже при обращении через функции get_numer и get_denom.</p>
10 <p>Последний способ обладает недостатком - вычисление нормальной формы происходит на каждый вызов. Избежать этого можно с помощью техники<strong>мемоизация</strong>.</p>
10 <p>Последний способ обладает недостатком - вычисление нормальной формы происходит на каждый вызов. Избежать этого можно с помощью техники<strong>мемоизация</strong>.</p>
11 <p>Учитывая новые вводные, становится понятно, что инвариант, который связывает конструктор и селекторы, нуждается в модификации. Функции get_numer и get_denom должны вернуть не переданные значения, а значения после нормализации:</p>
11 <p>Учитывая новые вводные, становится понятно, что инвариант, который связывает конструктор и селекторы, нуждается в модификации. Функции get_numer и get_denom должны вернуть не переданные значения, а значения после нормализации:</p>
12 <p>Если дробь уже нормализована, то это будут те же самые значения.</p>
12 <p>Если дробь уже нормализована, то это будут те же самые значения.</p>
13 <p>Абстракция не только прячет от нас реализацию, но и отвечает за соблюдение инвариантов. Если работать в обход абстракции, то внутренние преобразования не будут учтены:</p>
13 <p>Абстракция не только прячет от нас реализацию, но и отвечает за соблюдение инвариантов. Если работать в обход абстракции, то внутренние преобразования не будут учтены:</p>
14 <p>Если работать с данными напрямую, минуя абстракцию, можно сломать инварианты, которые обеспечивались дополнительной логикой в конструкторе или селекторах. Поэтому важно пользоваться кодом так, как было задумано авторами.</p>
14 <p>Если работать с данными напрямую, минуя абстракцию, можно сломать инварианты, которые обеспечивались дополнительной логикой в конструкторе или селекторах. Поэтому важно пользоваться кодом так, как было задумано авторами.</p>
15 <h2>Сокрытие данных</h2>
15 <h2>Сокрытие данных</h2>
16 <p>Можно сделать так, чтобы обойти абстракцию было нельзя. Такой подход называют<strong>сокрытием данных</strong>- data hiding.</p>
16 <p>Можно сделать так, чтобы обойти абстракцию было нельзя. Такой подход называют<strong>сокрытием данных</strong>- data hiding.</p>
17 <p>Чтобы обеспечить сокрытие, в языках используется специальный синтаксис. Однако защиту данных можно организовать и без специальных средств, только за счет функций высшего порядка. Такой способ основан на создании абстракций с помощью анонимных функций, замыканий и передачи сообщений. Если вы хотите узнать об этом больше, то пройдите курс<a>Python: Составные данные</a>.</p>
17 <p>Чтобы обеспечить сокрытие, в языках используется специальный синтаксис. Однако защиту данных можно организовать и без специальных средств, только за счет функций высшего порядка. Такой способ основан на создании абстракций с помощью анонимных функций, замыканий и передачи сообщений. Если вы хотите узнать об этом больше, то пройдите курс<a>Python: Составные данные</a>.</p>
18 <p>В реальности подобные механизмы легко обходятся с помощью Reflection API. Это можно сделать даже без них - за счет ссылочных данных. Также есть немало языков, например, JavaScript, в которых все нормально с абстракциями, но нет механизмов для защиты данных. При этом ничего страшного не произошло.</p>
18 <p>В реальности подобные механизмы легко обходятся с помощью Reflection API. Это можно сделать даже без них - за счет ссылочных данных. Также есть немало языков, например, JavaScript, в которых все нормально с абстракциями, но нет механизмов для защиты данных. При этом ничего страшного не произошло.</p>
19 <p>На практике при использовании абстракций никто не пытается специально их нарушать. Возможно, значение принудительной защиты данных сильно преувеличено.</p>
19 <p>На практике при использовании абстракций никто не пытается специально их нарушать. Возможно, значение принудительной защиты данных сильно преувеличено.</p>
20 <h2>Выводы</h2>
20 <h2>Выводы</h2>
21 <p>В этом уроке мы познакомились с инвариантами. Они используются, чтобы определять непротиворечивость состояния и существуют относительно любой операции. Также узнали, что такое нормализация - когда дробь приводится к нормальной форме. И выяснили, что можно сделать так, чтобы обойти абстракцию было нельзя. Это возможно с помощью<strong>сокрытия данных</strong>- data hiding.</p>
21 <p>В этом уроке мы познакомились с инвариантами. Они используются, чтобы определять непротиворечивость состояния и существуют относительно любой операции. Также узнали, что такое нормализация - когда дробь приводится к нормальной форме. И выяснили, что можно сделать так, чтобы обойти абстракцию было нельзя. Это возможно с помощью<strong>сокрытия данных</strong>- data hiding.</p>