기타

[Convention] 디미터 법칙(Law of Demeter)

bkuk 2023. 4. 22. 12:13

디미터 법칙이란?

객체지향 프로그래밍의 원칙 중 하나로, 객체 간의 결합도를 낮추고, 객체의 캡슐화를 강화하기 위한 원칙이다. 이를 간단하게 요약하면 "긴 객체 구조의 경로를 따라 멀리 떨어져 있는 간접적인(낯선) 객체에 메시지를 보내는(또는 이야기하는) 설계는 피하라는 것이다."라고 할 수 있다.

디미터 법칙에 따르면, 객체는 자신이 조작하는 객체의 내부 구조를 최소화하고, 다른 객체와의 상호작용을 제한해야 한다. 객체는 자신이 직접 소유하거나 관리하는 멤버에만 접근하고, 다른 객체의 내부 구조를 노출하는 것을 피해야 한다.

 

디미터 법칙의 주요 원칙

  1. 객체는 자신이 직접 소유하거나 관리하는 멈버에만 접근해야 한다.
  2. 객체는 다른 객체의 내부 구조를 노출하는 것을 피하고, 다른 객체와의 상호작용은 메서드를 통해서만 한다.
  3. 객체 간의 결합도를 최소화하여 객체들이 독립적으로 변경 및 확장될 수 있도록 해야 한다.
  4. 객체의 내부 구조를 숨기고, 외부에서 접근 강한 인터페이스를 제공하여 객체의 캡슐화를 강화해야 한다.

이러한 디미터 법칙을 준수하여 객체 간의 상호작용을 설계하면, 느슨하게 결합된 객체들로 구성된 유연하고 확장 가능한 코드를 작성할 수 있다.

 

디미터 법칙의 주요 원칙 1: 객체가 자신이 직접 소유하거나 관리하는 멤버에만 접근

class Student {
    private String name;
    private int age;
    private String grade;

    // 생성자와 게터 메서드 생략

    // 학생의 나이를 확인하는 메서드
    public boolean isAdult() {
        return age >= 18;
    }
}
class School {
    private List<Student> students;

    // 학생들의 목록을 반환하는 메서드
    public List<Student> getStudents() {
        return students;
    }

    // 학생들의 성인 여부를 출력하는 메서드
    public void printAdultStudentNames() {
        for (Student student : students) {
            if (student.isAdult()) {
                System.out.println("성인 학생 이름: " + student.getName());
            }
        }
    }
}

 

학생(Student) 클래스의 내부 구조에 직접 접근하지 않고, 학생 클래스가 제공하는 getter 메서드를 통해 멤버에 접근하고 있다. 학교(School) 클래스는 학생들의 목록을 관리하는데만 집중하고 있으며, 학생 클래스의 내부 구조에 대한 상세한 정보를 노출하지 않고, 필요한 정보만을 사용하는 방식으로 디미터 법칙을 따르고 있는 예시이다.

 

디미터 법칙의 주요 원칙 2: 객체는 다른 객체와의 상호작용은 메서드를 통해서만 한다.

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void printInfo() {
        System.out.println("학생 이름: " + name + ", 학생 나이: " + age);
    }
}
public class School {
    private List<Student> students; // 학교가 관리하는 학생 목록

    public School() {
        students = new ArrayList<>();
    }

    public void addStudent(Student student) {
        students.add(student);
    }

    public void removeStudent(Student student) {
        students.remove(student);
    }

    public void printStudentInfo() {
        for (Student student : students) {
            student.printInfo(); // 학생 객체의 정보를 출력하는 메서드를 호출
        }
    }
}

학교 클래스에서는 학생 목록을 관리하는 private 멤버 변수인 student에 대한 직접적인 접근만을 허용하고 있다. 또한, 학생 클래스에서는 학생의 이름과 나이를 출력하는 'printInfo()'라는 메서드를 제공하여 객체 간의 상호작용이 메서드를 통해서만 이루어지도록 하고 있다.

 

 

디미터 법칙의 주요 원칙 3: 객체 간의 결합도를 최소화하여, 독립적으로 변경 및 확장될 수 있도록 해야 한다.

// 주문 클래스
public class Order {
    private List<Item> items; // 주문에 포함된 상품 목록
    private Customer customer; // 주문을 한 고객

    public Order(Customer customer) {
        this.customer = customer;
        items = new ArrayList<>();
    }

    public void addItem(Item item) {
        items.add(item);
    }

    public void removeItem(Item item) {
        items.remove(item);
    }

    public void printOrderInfo() {
        System.out.println("주문 정보: ");
        for (Item item : items) {
            System.out.println(item.toString()); // 상품의 정보 출력
        }
        System.out.println("주문자: " + customer.toString()); // 주문자의 정보 출력
    }
}
// 상품 클래스
public class Item {
    private String name;
    private int price;

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return name + " - " + price + "원";
    }
}
// 고객 클래스
public class Customer {
    private String name;

    public Customer(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

 

위 코드 예시에서도, 'Item' 클래스와 'Customer' 클래스에서 필요한 정보를 'toString()'메서드를 통해서 반환하도록 설계 되어있다. 이를 통해 객체 간의 결합도를 최소화하여 독립적인 변경 및 확장이 가능하도록 디미터 법칙의 원칙을 적용한 예시이다.

 

디미터 법칙의 주요 원칙 3:객체의 내부 구조를 숨기고, 외부에서 접근 강한 인터페이스를 제공하여 객체의 캡슐화를 강화해야 한다.

// 학교 클래스
public class School {
    private List<Student> students; // 학교에 소속된 학생 목록

    // 생성자
    public School() {
        students = new ArrayList<>();
    }

    // 학생 추가
    public void addStudent(String name, int age, String studentId) {
        Student student = new Student(name, age, studentId);
        students.add(student);
    }

    // 학생 삭제
    public void removeStudent(String studentId) {
        Student studentToRemove = null;
        for (Student student : students) {
            if (student.getStudentId().equals(studentId)) {
                studentToRemove = student;
                break;
            }
        }

        if (studentToRemove != null) {
            students.remove(studentToRemove);
        }
    }

    // 학생 목록 출력
    public void printStudentList() {
        System.out.println("학생 목록:");
        for (Student student : students) {
            System.out.println("이름: " + student.getName());
            System.out.println("나이: " + student.getAge());
            System.out.println("학번: " + student.getStudentId());
            System.out.println();
        }
    }
}

// 학생 클래스
public class Student {
    private String name; // 학생의 이름
    private int age; // 학생의 나이
    private String studentId; // 학생의 학번

    // 생성자
    public Student(String name, int age, String studentId) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
    }

    // Getter 메서드
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getStudentId() {
        return studentId;
    }
}

 

참고: https://johngrib.github.io/wiki/jargon/law-of-demeter/