Вы когда-нибудь ловили себя на том, что перемещаетесь по нескольким файлам, чтобы определить, как ведет себя простая функция? С другой стороны, вы когда-нибудь были в середине хорошего класса, который делает все, пока вы изо всех сил пытаетесь добавить свое незначительное улучшение? Если вы были здесь раньше, возможно, код, который вы изучали, сильно связан и имеет низкую связность.
Итак, что именно является тесно связанным и низко связанным кодом? Почему тесно связанный код с низким уровнем связности — это плохо? И как вы можете написать лучший код?
Чтобы ответить на этот вопрос, давайте посмотрим на сцепление и сцепление.
Связь
В объектно-ориентированном дизайне связь относится к степени прямого знания одного элемента о другом. Другими словами, как часто изменения в классе А вызывают соответствующие изменения в классе Б.
Давайте посмотрим на этот фрагмент кода:
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 без дублирования кода.
Заключение
В заключение, слабосвязанный код с высокой связностью является лучшим. Кроме того, мы узнали, как сложно поддерживать тесно связанный код с низкой связностью и как его переработать.
Надеюсь, эта статья оказалась для вас полезной. Спасибо, что прочитали.
Подпишитесь на меня, чтобы получать обновления.
Вы также можете найти меня в другом месте в Интернете.