Вы когда-нибудь ловили себя на том, что перемещаетесь по нескольким файлам, чтобы определить, как ведет себя простая функция? С другой стороны, вы когда-нибудь были в середине хорошего класса, который делает все, пока вы изо всех сил пытаетесь добавить свое незначительное улучшение? Если вы были здесь раньше, возможно, код, который вы изучали, сильно связан и имеет низкую связность.
Итак, что именно является тесно связанным и низко связанным кодом? Почему тесно связанный код с низким уровнем связности — это плохо? И как вы можете написать лучший код?
Чтобы ответить на этот вопрос, давайте посмотрим на сцепление и сцепление.
Связь
В объектно-ориентированном дизайне связь относится к степени прямого знания одного элемента о другом. Другими словами, как часто изменения в классе А вызывают соответствующие изменения в классе Б.
Давайте посмотрим на этот фрагмент кода:
public class Order { private CashPayment payment = new CashPayment(); public void processPayment() { payment.process(); } } public class CashPayment { public void process() { // Process logic } }
Класс Order
напрямую зависит от класса CashPayment
, что затрудняет изменение класса CashPayment
без воздействия на класс Order
. В этом случае мы говорим, что класс Order
тесно связан с классом CashPayment
.
Чтобы сделать код слабо связанным, мы можем ввести абстракции в класс CashPayment
. Вот обновленная версия кода:
public class Order { private PaymentType payment; public Order(PaymentType paymentType) { payment = paymentType; } public void processPayment() { payment.process(); } } public interface PaymentType { public void process(); } public class CashPayment implements PaymentType { public void process() { // Process logic } }
В обновленном коде мы представили интерфейс PaymentType
, который определяет метод process()
. Класс Order
теперь зависит от интерфейса PaymentType
, а не от конкретного класса CashPayment
. Используя интерфейс, мы отделяем класс Order
от конкретной реализации класса CashPayment
.
Это изменение обеспечивает большую гибкость. Это также позволяет классу Order
работать с любым классом, реализующим интерфейс PaymentType
. В нашем случае мы можем легко добавить класс CreditCardPayement
, не изменяя класс Order
.
Подводя итог, можно сказать, что хороший код является слабосвязанным, поскольку модификации будут выполняться в связанном классе.
Сплоченность
В объектно-ориентированном проектировании сплоченность относится к тому, как проектируется отдельный класс. Сплоченность тесно связана с обеспечением того, чтобы класс был разработан с единственной, четко сфокусированной целью. Другими словами, если материалы, сгруппированные в класс, имеют тенденцию быть схожими во многих аспектах, то говорят, что класс имеет высокую сплоченность.
Давайте посмотрим на этот фрагмент кода:
public class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double getArea() { return 3.14 * radius * radius; } public double getPerimeter() { return 2 * 3.14 * radius; } public String toString() { return "Circle (radius: " + radius + ")"; } public void printDetails() { System.out.println("Shape: " + this.toString()); System.out.println("Area: " + getArea()); System.out.println("Perimeter: " + getPerimeter()); } }
Класс Circle
имеет атрибут radius
, а также методы вычисления площади, периметра и печати деталей окружности. Однако метод printDetails()
нарушает принцип единой ответственности, совмещая вывод деталей с вычислением площади и периметра. Мы говорим, что наш код имеет низкую связность.
Чтобы улучшить связность, мы можем разделить обязанности, создав новый класс только для отображения деталей круга:
public class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double getArea() { return 3.14 * radius * radius; } public double getPerimeter() { return 2 * 3.14 * radius; } public String toString() { return "Circle (radius: " + radius + ")"; } } public class CircleDetailsPrinter { private Circle circle; public CircleDetailsPrinter(Circle circle) { this.circle = circle; } public void printDetails() { System.out.println("Shape: " + circle.toString()); System.out.println("Area: " + circle.getArea()); System.out.println("Perimeter: " + circle.getPerimeter()); } }
В обновленном коде представлен класс CircleDetailsPrinter
для обработки печати деталей круга. Класс Circle
теперь делегирует ответственность за печать классу CircleDetailsPrinter
.
Выделяя функции печати в отдельный класс, мы говорим, что наш код в высшей степени связан. У каждого класса есть одна обязанность, что делает код более понятным и простым в обслуживании.
Обратите внимание, что в этом примере сплоченность улучшается за счет разделения обязанностей между двумя классами. Однако важно установить баланс и избежать чрезмерного разделения классов. Цель состоит в том, чтобы сфокусировать классы с четкими обязанностями, не разбивая их без необходимости на слишком много маленьких блоков.
Хороший код обладает высокой связностью, поскольку изменения будут выполняться в соответствующем классе.
Теперь, когда мы поняли концепцию связности, давайте применим то, что мы узнали о связности, к последнему примеру. Мы видим, что класс CircleDetailsPrinter
тесно связан с классом Circle
. Давайте отделим класс CircleDetailsPrinter
от конкретной реализации класса Circle
:
public interface Shape { public double getArea(); public double getPerimeter(); } public class Circle implements Shape { // ... } public class Square implements Shape { // ... } public class Rectangle implements Shape { // ... } public class ShapeDetailsPrinter { private Shape shape; public ShapeDetailsPrinter(Shape shape) { this.shape = shape; } public void printDetails() { System.out.println("Shape: " + shape.toString()); System.out.println("Area: " + shape.getArea()); System.out.println("Perimeter: " + shape.getPerimeter()); } }
Внедрив интерфейс Shape
, мы отделили класс CircleDetailsPrinter
от конкретной реализации класса Circle
. Кроме того, мы используем новый класс ShapeDetailsPrinter
для обслуживания классов Square
и Rectangle
без дублирования кода.
Заключение
В заключение, слабосвязанный код с высокой связностью является лучшим. Кроме того, мы узнали, как сложно поддерживать тесно связанный код с низкой связностью и как его переработать.
Надеюсь, эта статья оказалась для вас полезной. Спасибо, что прочитали.
Подпишитесь на меня, чтобы получать обновления.
Вы также можете найти меня в другом месте в Интернете.