На этой неделе я завершил свое первое полнофункциональное одностраничное приложение Rails backend/JavaScript frontend. Это, безусловно, мой самый большой проект на сегодняшний день с более чем 100 файлами, 8 моделями на бэкэнде и использованием полиморфных ассоциаций. Я выбрал изображение ниже, чтобы проиллюстрировать, как легко можно найти вещи, когда вы их систематизируете. В этом проекте я использовал классы и объектно-ориентированный JavaScript, чтобы мой код был невероятно хорошо организован, и обнаружил, что исправлять ошибки стало намного проще, чем когда-либо, поскольку я мог легко отследить источник ошибки в своем коде.

Поскольку это был мой первый крупный проект с использованием JavaScript, в этом посте я сосредоточусь только на внешней структуре моего приложения. Чтобы организовать свой код в чистоте, я использовал классы, по одному для каждой модели, а также класс API, который я использовал для вызовов бэкэнда. Наконец, у меня был основной файл app.js, который использовался для захвата элементов, а также для добавления прослушивателей событий.

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

class API {
    static baseURL = 'http://localhost:3000';
    static get(url) {
        return fetch(this.baseURL + url).then(function (response) {
          if (response.status !== 200) {
            throw new Error(response.statusText);
          }
          return response.json();
     });
    }

Мой класс API содержал статический метод уровня класса для каждой из стандартных глаголов HTTP-запроса (получить, опубликовать, исправить, удалить), и это позволило мне легко выполнять запросы на выборку в мою базу данных из любого места в других моих классах. Затем я мог бы использовать полученные данные JSON (поскольку я установил их в качестве возвращаемого значения) для вызова других методов или сохранения данных в моем интерфейсе. Я специально разработал методы класса API для получения конечной точки URL и данных, чтобы методы были максимально чистыми и могли вызываться из любого места.

createGame(params) {
    API.post('/games', params).then((data) =>
        this.removeDuplicatePlayers(data)
    );
}

Как вы можете видеть в приведенном выше фрагменте кода, я мог легко использовать данные (в данном случае данные, введенные в начальную форму запуска игры) и делать запросы к моему API. Затем я использую данные для вызова других методов в том же классе.

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

Я решил эту проблему, добавив статические значения в каждый из моих классов с именем «текущий (вставьте здесь имя класса)».

static currentGame;
save() {
    Game.currentGame = this;
}

Затем я создал методы save() как методы экземпляра, которые я мог бы затем вызвать, чтобы установить значение currentGame для этого конкретного экземпляра. Это очень пригодилось в моем приложении.

class Game {
    constructor(playerOrder, id) {
        this.playerOrder = playerOrder;
        this.id = id;
    }

В моем конструкторе класса я мог установить атрибуты, которые, как я знал, мне понадобятся. В случае с экземпляром игры я хотел всегда иметь возможность отслеживать playerOrder, чтобы, когда я закончу ход или раунд (в моих классах dropzone и round соответственно), я мог получить доступ к playerOrder, чтобы увидеть, кто идти дальше и, следовательно, какой div мне нужно сделать видимым, чтобы показать этому игроку свою руку. Добавив значение playerOrder, а затем вызвав метод save() для этого конкретного экземпляра, я смог получить доступ к playerOrder в любом месте моих 8 классов, вызвав следующее:

Game.currentGame.playerOrder

Я сделал то же самое с текущим раундом, я также отслеживал все детали игроков, такие как их идентификаторы, вытягивали ли они карты, размещали какие-либо наборы и многое другое.

endTurn() {
    let nextPlayerIndex = 0;
    const currentPlayerIndex = Game.currentGame.playerOrder.indexOf(
        Round.currentRound.currentPlayer
    );
    if (currentPlayerIndex < Game.currentGame.playerOrder.length - 1   ) {
        nextPlayerIndex = currentPlayerIndex + 1;
    }
    const screen = document.getElementById(
        `player${Round.currentRound.currentPlayer.id}`
    );
    screen.style.display = 'none';
    Round.currentRound.currentPlayer =
        Game.currentGame.playerOrder[nextPlayerIndex];
    const betweenTurns = document.querySelector('.betweenTurns');
    betweenTurns.style.display = 'flex';
    const betweenTurnsMessage =   document.querySelector('.betweenTurnsMessage');
    betweenTurnsMessage.textContent =  `${Round.currentRound.currentPlayer.name}'s Turn Will Begin  Shortly`;
    setTimeout(Game.currentGame.turnInfoUpdate, 5000);
}

Как вы можете видеть в методе endTurn() в моем классе Round. Я пользуюсь проверкой playerOrder, чтобы увидеть, какая позиция моего Round.currentRound.currentPlayer находится в этом порядке. Используя позицию индекса этого игрока, я могу проверить, какой игрок следующий. Затем я хватаюсь за их ручное div, используя информацию об их идентификаторах, которую я сохранил, я устанавливаю текущий (скоро будет предыдущий) div игрока так, чтобы он не отображал, я отображаю сообщение между ходами, в котором говорится, что (новое имя текущего игрока) Вскоре начнется поворот, а затем я запускаю функцию для обновления экрана, используя данные, которые я установил.

Как видите, классы могут быть чрезвычайно мощным инструментом, если их правильно использовать и писать. Я использовал классы для создания чрезвычайно организованного кода, который позволил 8 классам (9 включая мой класс API) работать вместе, делая очень мало вызовов API благодаря моим методам save(). Все мои ключевые данные были доступны мне в любой момент в любом из моих классов, и ошибки было очень легко отследить благодаря организованному способу, которым я устанавливал свой код.