Первой задачей было создание кортежей. Поскольку приложение будет иметь две разновидности кортежей (точки и векторы), я решил использовать кортеж фиксированной ширины вместо универсального. В общем случае вы можете определить, сколько элементов содержит кортеж (например, пара: кортеж из 2, тройка: кортеж из 3, четверка: кортеж из 4 и т. д.).
Фиксированная ширина, в данном случае означает четверку: (x, y, z, w), где w — тип кортежа (0 для векторов, 1 для точек).

Классы

Поскольку я использую Java, я подумал о реализации Tuple, Point и Vector как трех отдельных классов, Point и Vector, расширяющих Tuple. Но я отказался от этого (пока), и я не различаю шаблоны объектов: все теперь в Tuple.

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

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

Члены

Теперь, когда у меня есть один класс, я могу перейти к определению переменных-членов.

Первая идея состоит в том, чтобы все поля были целыми числами, закрытыми и добавлялись соответствующие геттеры и сеттеры, чтобы разрешить к ним доступ. Теперь я решил работать с неизменяемыми объектами. Следовательно, все члены были окончательными; это устраняет сеттеры. Для таких простых конструкций я не являюсь поклонником соглашения о стиле кодирования Java Bean (геттеры). Я знаю, что модули отражения и утилиты часто полагаются на эту характеристику, но я не планирую использовать такие библиотеки. Для меня нормально давать публичный доступ к полям — и они окончательные, чтобы никто не мог изменить их значение.
Затем я превратил x, y, z в плавающие, потому что позже они будут больше полезно[2].

Создание объекта

Класс Tuple имеет частный конструктор пакета. Я использовал это решение, потому что не видел никакого варианта использования для создания экземпляров объектов Tuple (имеющих значение w, отличное от 1 или 0) вне пакета, в котором они находятся. Почему не частный застройщик? Потому что я хочу создавать объекты Tuple в тестах, чтобы убедиться, что создание точек и векторов работает нормально, и в тестах я могу использовать любые кортежи, а не только векторы и точки.

Для удобства создания точек и векторов я ввел новый тип: TupleFactory. Я основывал его на фабричном шаблоне; у него было два метода: векторный и точечный. Их реализация была проста:

Опять же, в классе было не так много кода, и все, что я делал, — это делегировал вызов конструктору. Поэтому я решил исключить этот класс и переместить фабричные методы в сам класс Tuple.

Я знаю, что такие решения были бы лучше в одноэлементных объектах, где вы бы вызвали getInstance() и получили единственный доступный объект. Но я подумал, что лучше иметь эти два фабричных метода в классе Tuple, потому что, в конце концов, все, что я получаю, — это два типа кортежей.

Операции

Последним требованием было реализовать операции над этими кортежами.

Я не буду вдаваться в подробности, потому что вы можете увидеть мое решение в моем репозитории GitHub. Единственное, чем я хочу с вами поделиться, это то, что мне нужно было перейти от базовых типов с плавающей запятой к десятичным числам. Это потому, что Math.sqrt() возвращает десятичное число, и я не хотел приводить типы после каждой операции.

Каждый метод работает с кортежем или возвращает его, кроме калькулятора векторного произведения. Он работает с двумя векторами и возвращает вектор. Я думал о том, чтобы реализовать его как можно более широко, но мои знания в этой области недостаточны[3], и я не хочу создавать туфту. И в книге тесты разработаны на векторах, поэтому я предпринял действия и определил этот метод для возврата вектора — независимо от входных параметров.

Вывод

В этой статье была рассмотрена глава 2 книги[4] и мой подход к этому проекту. Я знаю, что трудно вносить свой вклад, не зная требований, но мне интересно ваше мнение. Не стесняйтесь комментировать, если вам нравятся или не нравятся мои классы, создание экземпляров, общий дизайн.
Помните: есть много правильных решений!

Код для этой части доступен в теге chapter_2 моего репозитория:



В следующей статье я продолжу главу 3, где в игру вступают матрицы. Быть в курсе!

[1] Помните, я работаю с четверками, потому что это фиксированное требование для всего приложения.

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

[3] Я 13 лет назад кое-что узнал на эту тему в университете, но все забыл. И я не хочу гуглить решение, по крайней мере, не сейчас.

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