Абстрагирование связанной функциональности с использованием интерфейсов и жесткой связи

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

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

interface iFormatter()
{
public function format(array $order): array
}

public class OrderFormatter implements iFormatter
{
public function format(array $order): array
{
// ...

return $formattedArray;
}
}

public class OrderGenerator implements iGenerator
{
private $formatter;

public function __construct(iFormatter $formatter)
{
$this->formatter = $formatter;
}

public function generate()
{
// ...

return $this->formatter->format($order);
}
}

Так что я думаю, что в случае, когда только форматер изменится, это будет определено как слабосвязанное;

$example = new OrderGenerator(new CarOrderFormatter);
$example->generate();

$example = new OrderGenerator(new VanOrderFormatter);
$example->generate();

Однако я не совсем понимаю, когда вы отвлекаете друг друга от обязанностей, но эти классы все еще тесно связаны друг с другом. Что-то вроде

$example = new CarOrderGenerator(new CarOrderFormatter);
$example->generate();

$example = new VanOrderGenerator(new VanOrderFormatter);
$example->generate();

Да, вы можете передать другой форматер этим генераторам, но, несомненно, чаще, чтобы в format функция, если он ожидает определенных данных от generate функция в XXXOrderGenerator конкретный класс?

Так что я верить Я абстрагировал обязанности от их собственных классов в последнем примере выше, и хотя интерфейсы были использованы, я не уверен, что это все еще тесно связано или технически слабо связано? Или если я полностью упустил суть …

9

Решение

Говорят, что классы тесно связаны, когда один класс зависит от другого или полагается на внутреннее устройство или специфику другого.

В первом примере, который вы указали, OrderGenerator и OrderFormatter тесно не связаны, поскольку ни один из них не зависит от другого: они оба зависят от iFormatter. Другими словами, OrderFormatter ничего не знает о OrderGenerator, а OrderGenerator ничего не знает о OrderFormatter, он просто знает, что переданный ему объект реализует функцию ‘format’. Вы отметили это, когда передаете или CarOrderFormatter или VanOrderFormatter в OrderGenerator без отрицательного результата.

В случае второго примера, когда CarOrderFormatter передается в CarOrderGenerator, вы упомянули, что наверняка произойдут ошибки, если вы передадите, скажем, VanOrderFormatter в CarOrderGenerator. Если код в CarOrderGenerator полагается на детали реализации CarOrderFormatter, то классы тесно связаны, хотя интерфейс iFormatter — это то, что видит CarOrderGenerator. Этот код может сбить с толку, потому что CarOrderGenerator говорит со своим «контрактом», что его не волнует, какой форматер передается, но явно он делает. Поэтому было бы лучше с точки зрения ясности кода, если CarOrderGenerator явно сказал, что вы можете только передать CarOrderFormatter его конструктору. Абстракция (то есть использование интерфейса) не является плохой вещью, однако, и необходимо подумать, как именно должен быть определен интерфейс. Например, вместо того, чтобы иметь только интерфейс iFormatter, вместо этого у вас были iCarOrderFormatter и iVanOrderFormatter, которые определены одинаково, но CarOrderGenerator требовал iCarOrderFormatter, а VanOrderGenerator требовал iVanOrderFormatter. Ваш пример может стать:

$example = new CarOrderGenerator(new CarOrderFormatter
$example->generate();
$example = new CarOrderGenerator(new SportsCarOrderFormatter)
$example->generate();

или же

$example = new VanOrderGenerator(new PassengerVanOrderFormatter
$example->generate();
$example = new VanOrderGenerator(new CargoVanOrderFormatter)
$example->generate();

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

6

Другие решения

Вы правы в том, что если вы столкнетесь с бетонами автомобилей / фургонов для iGenerator и iFormatter, вы, вероятно, столкнетесь с проблемами при смешивании зависимостей. Но дело в том, что вы не должны сталкиваться с этой ситуацией, если вы делаете это, вы должны вернуться к чертежной доске, потому что вы сделали неправильный поворот.

Я не знаю, является ли ваш пример надуманным или основанным (или нет) на вашей проблемной области. В любом случае проблема в плохом дизайне. Ваш интерфейс должен представлять одну ответственность или цель. Для этого какова цель iOrderGenerator и iFormatter? Ваш пример, все, что делает iOrderGenerator — это обертка iFormatter. Я мог бы реорганизовать iOrderGenerator из дизайна, потому что он не имеет смысла. Проблема. Решаемые.

Хорошо, давайте не будем выбирать легкий путь и скажем, что у iOrderGenerator была цель. Ну, эта цель должна отличаться от iFormatter. Если ваша реализация интерфейса выходит за границы его единственного назначения или повторяет назначение другого интерфейса, вероятно, у вас плохой дизайн. В качестве примера, скажем, целью iFormatter является печать заказов по-разному в зависимости от типа заказа (фургон или автомобиль). Тогда iOrderGenerator отвечает за создание заказов (любого типа). Но у меня может быть несколько способов создать заказ. CsvOrderGenerator или XmlOrderGenerator или BulkOrderGenerator (но никогда не CarOrderGen или VanOrderGen).

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

Вы iFormatter плохой интерфейс, потому что он возвращает неопределенное значение (массив). Все, что использует возвращаемое значение, должно обладать глубокими знаниями о том, как работает бетон, чтобы знать, какие элементы были или не содержались в массиве. Т.е. как бы я узнал, что было значение ключа orderId без чтения кода или просмотра результата (отладка / печать). Вместо этого iFormatter должен возвращать фиксированный, хорошо описанный тип, т.е. CarFormat или VanFormat, каждый из которых может реализовывать iFormat, который предоставляет общие средства доступа для номера заказа и цены.

iOrderGenerator, вероятно, должен генерировать iOrder. Который может иметь метод getFormatter: iFormatter.

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

1

По вопросам рекламы [email protected]