📅 학습 기간: 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 및 결합도 & 응집도의 개념을 확실히 정리할 수 있었다! 🎯
'IT > JAVA' 카테고리의 다른 글
[JAVA] 자바 기초 복습 (Chapter.13) (0) | 2025.03.05 |
---|---|
[JAVA] 자바 기초 복습 (Chapter.12) (1) | 2025.03.04 |
[JAVA] 자바 기초 복습 (Chapter.10) (1) | 2025.03.02 |
[JAVA] 자바 기초 복습 (Chapter.09) (0) | 2025.03.02 |
[JAVA] 자바 기초 복습 (Chapter. 08) (1) | 2025.03.01 |