Не обращайте внимания на все ошибки OO и начните с простой структуры с открытыми членами данных.Это наименьшая работа, самая простая для понимания, самая простая в использовании и самая мощная.Когда вы лучше поймете контекст использования и уверены, что класс действительно представляет абстракцию, тогда примените консервативные ограничения и внимательно наблюдайте, где ваш код ломается (если есть).
Если у вас нет абсолютных доказательств, подкрепленных десятилетиями тяжелой работыматематика, не изначально используйте абстракции.Прежде чем изолировать представление от клиента, вы должны быть полностью убеждены, что ваша абстракция является правильной и полной.Убедитесь, что в утверждениях или хотя бы в комментариях указаны инварианты представления.Если вы не можете сделать это, вы не понимаете достаточно для создания абстракции.
Например (в C ++, извините):
class Rational {
int numerator; int denominator;
public:
Rational (int x, int y) {
if (y == 0) { throw DivisionByZero; }
assert (y != 0);
if (y<0) { x = -x; y = -y; }
assert (y>0);
int d = gcd(x,y);
assert(d>0); // proof, from specs of gcd
// assert: if there exists an integer q>0 which divides both x/d and y/d
// then q == 1 (from definition of gcd)
assert (x % d == 0); // d divides x exactly (from defn of gcd)
assert (y % d == 0); // d divides y exactly (from defn of gcd)
numerator = x/d;
denominator = y/d;
// assert: provided y != 0, numerator / denominator (math div, not C div)
// equals input x/ input y (math div, not C div).
// invariant: denominator > 0, gcd (numerator, denominator) == 1
// Theorem: representation is unique
}
bool eq(Rational other) {
numerator == other.numerator && denominator == other.denominator;
// sufficient by uniqueness theorem
}
}:
В этом конкретном случае вы должны отметить вычислительную стоимостьобеспечение сохранения инварианта представления, особенно при реализации, скажем, сложения.Возможно, это не очень хорошая идея.Все зависит от того, как выглядит ваш интерфейс и как вы собираетесь его использовать.Сохранение инварианта имеет одно полезное свойство в вычислительном отношении, оно предотвращает предотвращаемые переполнения при умножении.
В этом случае представление представляет собой стандартную каноническую форму следующего типа:
R: int * int - { x,y | y<0 or Exists d. d divides y and d divides x }
Это типичното, что представление состоит из декартового произведения минус подмножество, чтобы произвести множество R такое, что
R <==> A
, где A - абстрактный тип, является биекцией.
Моя точка зрения: не так просто правильно получить абстракцию.Программисты злоупотребляют сильной абстракцией, создавая такие устройства, как классы, особенно в ОО-языках, когда они не подозревают, что, например, простая структура, представляющая собой декартово произведение, уже формально абстрактна (представлена функциями, в данном случае проекциями).
Исправление неисправной абстракции - это гораздо больше работы с точки зрения настройки не только интерфейса класса, но и каждого его использования, чем если бы вы использовали простую структуру и систематически абстрагировали ее на этапе рефакторинга, когда у вас есть доказательствао том, какой на самом деле должна быть абстракция.
Правило Иттрила: если вы думаете об абстракции .. не надо!
Вам нужны доказательства, а не мысли!