Книга: Чистая архитектура. Искусство разработки программного обеспечения
Назад: 25. Уровни и границы
Дальше: 27. Службы: большие и малые

26. Главный компонент

Martin_Page_257_Image_0001.tif 

В каждой системе имеется хотя бы один компонент, который создает другие компоненты, наблюдает за ними и координирует их действия. Я называю такой компонент главным (Main).

Конечная деталь

Компонент Main — это конечная деталь, политика самого низкого уровня. Он является точкой входа в систему. От него ничего не зависит, кроме работоспособности системы. Его задача — создать все Фабрики, Стратегии и другие глобальные средства и затем передать управление высокоуровневым абстракциям в системе.

Именно в компоненте Main должны внедряться все зависимости с использованием инфраструктуры внедрения зависимостей. После этого компонент Main должен распространить эти зависимости, обычно без использования инфраструктуры.

Компонент Main можно считать самым грязным из всех грязных компонентов.

Рассмотрим следующий компонент Main из последней версии игры «Охота на Вампуса». Обратите внимание, как он загружает все строки, о которых не должен знать основной код.

public class Main implements HtwMessageReceiver {

  private static HuntTheWumpus game;

  private static int hitPoints = 10;

  private static final List<String> caverns =

      new ArrayList<>();

  private static final String[] environments = new String[]{

    "bright",

    "humid",

    "dry",

    "creepy",

    "ugly",

    "foggy",

    "hot",

    "cold",

    "drafty",

    "dreadful"

  };

 

  private static final String[] shapes = new String[] {

    "round",

    "square",

    "oval",

    "irregular",

    "long",

    "craggy",

    "rough",

    "tall",

    "narrow"

  };

 

  private static final String[] cavernTypes = new String[] {

    "cavern",

    "room",

    "chamber",

    "catacomb",

    "crevasse",

    "cell",

    "tunnel",

    "passageway",

    "hall",

    "expanse"

  };

 

  private static final String[] adornments = new String[] {

    "smelling of sulfur",

    "with engravings on the walls",

    "with a bumpy floor",

    "",

    "littered with garbage",

    "spattered with guano",

    "with piles of Wumpus droppings",

    "with bones scattered around",

    "with a corpse on the floor",

    "that seems to vibrate",

    "that feels stuffy",

    "that fills you with dread"

  };

Далее следует функция main. Обратите внимание, как она использует HtwFactory для создания игры. Она передает имя класса, htw.game.HuntTheWumpusFacade, потому что этот класс даже грязнее, чем Main. Это предотвращает изменения в данном классе из-за повторной компиляции/развертывания Main.

public static void main(String[] args) throws IOException {

  game = HtwFactory.makeGame("htw.game.HuntTheWumpusFacade",

                             new Main());

  createMap();

  BufferedReader br =

    new BufferedReader(new InputStreamReader(System.in));

  game.makeRestCommand().execute();

  while (true) {

    System.out.println(game.getPlayerCavern());

    System.out.println("Health: " + hitPoints + " arrows: " +

                       game.getQuiver());

    HuntTheWumpus.Command c = game.makeRestCommand();

    System.out.println(">");

    String command = br.readLine();

    if (command.equalsIgnoreCase("e"))

      c = game.makeMoveCommand(EAST);

    else if (command.equalsIgnoreCase("w"))

      c = game.makeMoveCommand(WEST);

    else if (command.equalsIgnoreCase("n"))

      c = game.makeMoveCommand(NORTH);

    else if (command.equalsIgnoreCase("s"))

      c = game.makeMoveCommand(SOUTH);

    else if (command.equalsIgnoreCase("r"))

      c = game.makeRestCommand();

    else if (command.equalsIgnoreCase("sw"))

      c = game.makeShootCommand(WEST);

    else if (command.equalsIgnoreCase("se"))

      c = game.makeShootCommand(EAST);

    else if (command.equalsIgnoreCase("sn"))

      c = game.makeShootCommand(NORTH);

    else if (command.equalsIgnoreCase("ss"))

      c = game.makeShootCommand(SOUTH);

    else if (command.equalsIgnoreCase("q"))

      return;

 

    c.execute();

  }

}

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

Наконец, посмотрите, как main создает карту подземелий.

  private static void createMap() {

    int nCaverns = (int) (Math.random() * 30.0 + 10.0);

    while (nCaverns-- > 0)

      caverns.add(makeName());

      for (String cavern : caverns) {

        maybeConnectCavern(cavern, NORTH);

        maybeConnectCavern(cavern, SOUTH);

        maybeConnectCavern(cavern, EAST);

        maybeConnectCavern(cavern, WEST);

      }

 

    String playerCavern = anyCavern();

    game.setPlayerCavern(playerCavern);

    game.setWumpusCavern(anyOther(playerCavern));

    game.addBatCavern(anyOther(playerCavern));

    game.addBatCavern(anyOther(playerCavern));

    game.addBatCavern(anyOther(playerCavern));

 

    game.addPitCavern(anyOther(playerCavern));

    game.addPitCavern(anyOther(playerCavern));

    game.addPitCavern(anyOther(playerCavern));

 

    game.setQuiver(5);

  }

 

  // здесь следует еще много кода...

}

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

Заключение

Представьте, что Main является плагином для приложения — плагином, который настраивает начальное окружение, загружает все внешние ресурсы и затем передает управление политике верхнего уровня. Так как это плагин, может иметься множество компонентов Main, по одному для каждой конфигурации приложения.

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

Когда вы начинаете думать о компоненте Main как о плагине, находящемся за архитектурной границей, проблемы настройки решаются намного проще.

Назад: 25. Уровни и границы
Дальше: 27. Службы: большие и малые