IT/JAVA

[JAVA] 자바 기초 복습 (Chapter.11)

j8970 2025. 3. 3. 22:15

📅 학습 기간: 2월 2주차
🎯 학습 내용: OOP / 결합도 & 응집도


📌 1. OOP (객체 지향 프로그래밍)

💡 객체 지향 프로그래밍의 5가지 원칙인 SOLID 원칙을 기반으로 소프트웨어 설계
유지 보수성과 확장성을 높이기 위한 핵심 개념


🔹 1.1 SOLID 원칙

원칙 설명
SRP (단일 책임 원칙) 클래스는 단 하나의 책임만 가져야 한다
OCP (개방-폐쇄 원칙) 확장에는 열려 있고, 수정에는 닫혀 있어야 한다
LSP (리스코프 치환 원칙) 상위 클래스 객체를 하위 클래스 객체로 치환 가능해야 한다
ISP (인터페이스 분리 원칙) 하나의 큰 인터페이스보다 여러 개의 작은 인터페이스를 사용하는 것이 좋다
DIP (의존 역전 원칙) 고수준 모듈이 저수준 모듈에 의존하지 않고, 추상화에 의존해야 한다

OOP의 4가지 특징 (상속, 추상화, 다형성, 캡슐화)와 함께 사용됨


🔹 1.2 SRP (단일 책임 원칙)

// ❌ 잘못된 예제 - 하나의 클래스가 여러 개의 책임을 가짐
class SchoolHelper {
    void cleanClassroom() {
        System.out.println("교실을 청소합니다");
    }

    void prepareLunch() {
        System.out.println("급식을 준비합니다.");
    }
}

// ✅ 올바른 예제 - 각각의 클래스가 역할을 담당
class Janitor {
    void cleanClassroom() {
        System.out.println("교실을 청소합니다.");
    }
}

class LunchStaff {
    void prepareLunch() {
        System.out.println("급식을 준비합니다.");
    }
}

각 클래스가 단일 책임을 가지도록 분리


🔹 1.3 OCP (개방-폐쇄 원칙)

// ❌ 잘못된 예제 - 기존 코드에 대한 직접적인 수정
class LunchMenu {
    void serveLunch(String studentType) {
        if (studentType.equals("일반 학생")) {
            System.out.println("알레르기가 없습니다.");
        } else if (studentType.equals("주의 학생")) {
            System.out.println("특별 배식구로 가주세요.");
        }
    }
}

// ✅ 올바른 예제 - 기존 코드를 수정하지 않고, 새로운 기능 확장
interface LunchMenuInterface {
    void serveLunch();
}

class RegularStudent implements LunchMenuInterface {
    @Override
    public void serveLunch() {
        System.out.println("일반 학생 - 알레르기가 없습니다.");
    }
}

class AllergyStudent implements LunchMenuInterface {
    @Override
    public void serveLunch() {
        System.out.println("주의 학생 - 특별 배식구로 이동하세요.");
    }
}

추상화를 활용하여 새로운 기능을 추가할 때 기존 코드 수정 없이 확장 가능


🔹 1.4 LSP (리스코프 치환 원칙)

// ❌ 잘못된 예제 - 부모의 기능을 자식이 대체하지 못함
class Student {
    void playSoccer() {
        System.out.println("축구를 할 수 있습니다.");
    }
}

class InjuredStudent extends Student {
    void playSoccer() {
        System.out.println("다쳐서 축구를 할 수 없습니다.");
    }
}

// ✅ 올바른 예제 - 추상화를 사용하여 동작 일관성 유지
abstract class StudentAbstract {
    abstract void activity();
}

class SoccerPlayer extends StudentAbstract {
    @Override
    void activity() {
        System.out.println("축구를 합니다.");
    }
}

class InjuredSoccerPlayer extends StudentAbstract {
    @Override
    void activity() {
        System.out.println("다쳐서 축구를 할 수 없습니다. 재활 훈련을 합니다.");
    }
}

부모 클래스의 기능을 일관되게 유지하면서, 자식 클래스에서 확장 가능


🔹 1.5 ISP (인터페이스 분리 원칙)

// ❌ 잘못된 예제 - 인터페이스가 너무 많은 책임을 가짐
interface Worker {
    void work();
    void eat();
}

class Robot implements Worker {
    @Override
    public void work() {
        System.out.println("로봇은 일을 할 수 있습니다.");
    }
    @Override
    public void eat() {
        System.out.println("로봇은 음식을 먹지 않습니다.");
    }
}

// ✅ 올바른 예제 - 작은 인터페이스로 분리
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Employee implements Workable, Eatable {
    @Override
    public void work() {
        System.out.println("9시부터 18시까지 일을 합니다.");
    }
    @Override
    public void eat() {
        System.out.println("12시부터 13시까지 점심시간 입니다.");
    }
}

class RobotClass implements Workable {
    @Override
    public void work() {
        System.out.println("로봇이 일을 합니다.");
    }
}

인터페이스를 분리하여 필요 없는 기능을 구현하지 않도록 개선


🔹 1.6 DIP (의존 역전 원칙)

// ❌ 잘못된 예제 - 클래스가 특정 구현에 의존
class MathBook {
    void read() {
        System.out.println("수학책을 읽습니다.");
    }
}

class DIPStudent {
    private MathBook book;

    public DIPStudent() {
        this.book = new MathBook();
    }

    void study() {
        book.read();
    }
}

// ✅ 올바른 예제 - 추상화에 의존하도록 설계
interface Book {
    void read();
}

class ScienceBook implements Book {
    public void read() {
        System.out.println("과학책을 읽습니다.");
    }
}

class KoreanBook implements Book {
    public void read() {
        System.out.println("국어책을 읽습니다.");
    }
}

class CorrectStudent {
    private Book book;

    public CorrectStudent(Book book) {
        this.book = book;
    }

    void study() {
        book.read();
    }
}

구체적인 구현이 아닌 추상화에 의존하도록 설계하여 확장성 향상


📌 2. 결합도 & 응집도 (Coupling & Cohesion)

💡 소프트웨어 모듈의 독립성을 결정하는 중요한 개념
낮은 결합도(Loose Coupling) + 높은 응집도(High Cohesion) 가 이상적인 설계


🔹 2.1 결합도 (낮은 결합도)

interface Teacher {
    void teach();
}

class KoreanTeacher implements Teacher {
    @Override
    public void teach() {
        System.out.println("국어 선생님은 국어를 가르칩니다.");
    }
}

class Classroom {
    private Teacher teacher;

    public Classroom(Teacher teacher) {
        this.teacher = teacher;
    }

    public void startClass() {
        teacher.teach();
    }
}

인터페이스를 활용하여 결합도를 낮추고 유지보수성을 높임


🔹 2.2 응집도 (높은 응집도)

💡 응집도(Cohesion)는 모듈(클래스)이 얼마나 관련 있는 기능들로 구성되어 있는지를 나타냄
높은 응집도를 가지면 모듈이 하나의 역할에 집중하여 유지보수성이 향상됨
낮은 응집도를 가지면 하나의 모듈이 여러 가지 기능을 담당하여 복잡성이 증가함

✅ 낮은 응집도 예제 (잘못된 예제)

class Student {
    void study() {
        System.out.println("공부를 합니다.");
    }

    void play() {
        System.out.println("운동을 합니다.");
    }

    void cleanClassroom() {
        System.out.println("교실을 청소합니다.");
    }
}

Student 클래스가 공부, 운동, 청소까지 담당하여 여러 역할이 섞여 있음
클래스가 너무 많은 책임을 가지고 있어 유지보수가 어려움


✅ 높은 응집도 예제 (올바른 예제)

class Student {
    void study() {
        System.out.println("공부를 합니다.");
    }
}

class Athlete {
    void play() {
        System.out.println("운동을 합니다.");
    }
}

class Janitor {
    void cleanClassroom() {
        System.out.println("교실을 청소합니다.");
    }
}

각 클래스가 자신의 역할에 집중하여 높은 응집도를 가짐
유지보수와 코드의 재사용성이 향상됨


🔹 2.3 결합도 & 응집도의 관계

💡 낮은 결합도 + 높은 응집도
각 클래스나 모듈이 독립적으로 동작하면서 본인의 역할에 충실
클래스 간의 의존성을 줄이고, 변경에 강한 구조를 유지 가능

class StudentCohesion { 
    private String name;

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

    public String getName() { return name; }
}

class Attendance {
    public void checkAttendance(Teacher teacher, StudentCohesion student) {
        System.out.println(student.getName() + " 학생이 출석하였습니다.");
        teacher.teach();
    }
}

각 클래스가 명확한 역할을 가지며, 독립적으로 동작할 수 있도록 설계


🎯 소감

📌 SOLID 원칙을 이해하면 유지보수하기 쉬운 소프트웨어를 설계할 수 있다!
📌 낮은 결합도와 높은 응집도를 유지하는 것이 중요하다!
📌 OOP 설계를 잘 활용하면 코드의 확장성과 유연성을 높일 수 있다!

🚀 마무리
이번 학습을 통해 OOP 및 결합도 & 응집도의 개념을 확실히 정리할 수 있었다! 🎯