Coupling & Cohesion

  • Coupling (Связанность)
    насколько сильно связаны друг с другом отдельные модули.
  • Cohesion (Сочетаемость)
    насколько сильны связи внутри модуля.

CouplingVsCohesion.png

Coupling

Content Coupling

Возникает, когда один модуль полагается на внутренние особенности реализации другого модуля.

public class ShopService {

  public void addNewItem(Order order, OrderItem newItem) {
    order.getItems().add(newItem);
    order.setSum(order.getSum() + newItem.getPrice());
  }
}
public class ShopService {

  public void addNewItem(Order order, OrderItem newItem) {
    order.addItem(newItem);
  }
}

Law of Demeter

Объект должен иметь как можно меньше представления о структуре и свойствах другого объекта.

demeter-law.png

Common Coupling

Возникает между модулями, когда они работают с общими данными читая и изменяя их.

common-coupling-ex1.png

Control Coupling

Возникает, когда один модуль управляет поведением другого, через передачу каких-то данных или флагов управления.

class OrderService {
  private ReportService reportService;

  public void placeOrder() {
    // ...
    reportService.generate(order, ReportType.PDF);
  }
}
class ReportService {

  public void generate(Order data, ReportType type) {
    switch (type) {
        case XML:
            buildXmlReport(data);
            break;
        case PDF:
            buildPdfReport(data);
            break;
    }
  }

  private void buildXmlReport() { ... }
  private void buildPdfReport() { ... }
}
class OrderService {
  private ReportService reportService;

  public void placeOrder() {
    // ...
    reportService.generate(order);
  }
}
public interface ReportService {
    void generate(Order order);
}

public class XmlReportBuilder implements ReportService {  }
public class PdfReportBuilder implements ReportService {  }

control-coupling.png

Stamp Coupling

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

class ValidatorService {

    public boolean validateEmail(Customer customer) {
        var email = customer.getEmail();
        return EMAIL_REGEX.matcher(email).find();
    }
}
class Customer {
    private String firstName;
    private String lastName;
    private LocalDate birthDate;
    private String livingAddress;
    private String email;
}
class ValidatorService {

    public boolean validateEmail(String email) {
        return EMAIL_REGEX.matcher(email).find();
    }
}

Data Coupling

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

Message coupling

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

Cohesion

Coincidental cohesion

Слабейший из видов сочетаемости. Когда элементы внутри модуля собраны по случайному принципу и никак друг с другом не связаны.

Logical cohesion

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

  📁 dao
  📁 service
  📁 model
  ☕ Application.java
  📁 dao
    ☕ OrderDao.java
    ☕ UserDao.java
    ☕ PostDao.java
  📁 service
    ☕ OrderService.java
    ☕ UserService.java
  📁 model
    ☕ User.java
    ☕ Order.java
    ☕ OrderItem.java
    ☕ OrderState.java
    ☕ Comment.java
    ☕ Post.java
  ☕ Application.java
  📁 orders
    ☕ Order.java
    ☕ OrderItem.java
    ☕ OrderState.java
    ☕ OrderDao.java
    ☕ OrderService.java
  📁 users
    ☕ User.java
    ☕ UserDao.java
    ☕ UserService.java
  📁 reviews
    ☕ Post.java
    ☕ Comment.java
    ☕ PostDao.java
  ☕ Application.java
public MessageSenderService {

    public void sendOrderProcessedEvent() { ... }
    public void sendReportMessage() { ... }
    public void sendEmailNotification() { ... }
}

Temporal cohesion

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

public interface ApplicationInitializer {
    void initDatabase();
    void initPrinterService();
    void initFtpSerivce();
}

Procedural cohesion

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

public class RegistrationService {

    public void registerUser(String email) {
        validateEmail(email);
        User user = createNewUser(email);
        loadProfileFromFacebook(user);
        checkVipStatus(user);
        sendGreetings(email, user.getName());
    }
}
public interface UserService {
    void validateEmail(String email);
    User createNewUser(String email);
    void loadProfileFromFacebook(User user);
    void checkVipStatus(User user);
    void sendGreetings(String email, String name);
}

proc-refactoring.png

Sequential cohesion

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

public class RegistrationService {

    public void registerUser(String emailStr) {
        Email email = validateEmail(emailStr);
        User user = createNewUser(email);
        UserProfile profile = loadProfileFromFacebook(user);
        VipStatus vipStatus = checkVipStatus(user);
        sendGreetings(profile, vipStatus);
    }
}
public interface UserService {
    Email validateEmail(String email);
    User createNewUser(Email email);
    UserProfile loadProfileFromFacebook(User user);
    VipStatus checkVipStatus(User user);
    void sendGreetings(UserProfile profile, VipStatus vipStatus);
}

Communication cohesion

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

interface OrderService {
    public void addItem(Order order, Item item);
    public void deleteAllItems(Order order);
    public Money calculateTotalSum(Order order);
    public void startDelivery(Order order);
}
interface ComputerFactory {
    public Computer newServer(Integer ram, Integer hdd, Integer cpu);
    public Computer newPc(Monitor monitor, Mouse mouse, SystemUnit unit);
    public Computer newNotebook(Model model);
}
class User {
    private Long id;
    private Image avatar;
    private String email;
    private List<Orders> orders;
    private List<Review> reviews;

    public void changeAvatar(Image image) { ... }
    public void resetPassword() { ... }
    public void placeNewOrder(Order order) { ... }
    public void postReview(Review review) { ... }
    public void assignVipStatus() { ... }
}

oop-at-home.png

Functional cohesion

Возникает, когда элементы модуля сгруппированы вместе, так как все они вносят вклад в выполнение одной и той же функции.

interface OrderService {
    public void addItem(Order order, Item item);
    public void deleteAllItems(Order order);
    public Money calculateTotalSum(Order order);
    public void startDelivery(Order order);
}
interface OrderCart {
    public void addItem(Order order, Item item);
    public void deleteAllItems(Order order);
}

interface OrderCostCalculator {
    public Money calculateTotalSum(Order order);
}

interface OrderDelivery {
    public void startDelivery(Order order);
}

Low coupling & High cohesion

Модули, которые следуют принципам слабой связанности и высокой сочетаемости, обладают следующими свойствами:

  • Изменения в одном модуле не влияют на остальные модули
  • Проще разбираться в коде модуля, без необходимости изучать остальные модули
  • Удобство в переиспользовании

Design is About Balancing Cohesion and Coupling (not blindly following principles) – Copeland