객체 지향 프로그래밍 입문 강의 | 최범균 - 인프런
최범균 | 잘 하는 개발자가 되기 위해서는 유연한 코드를 작성할 줄 알아야합니다. 객체 지향을 이용해서 변경하기 좋은 유연한 코드를 만드는 방법을 알아보세요., 좋은 코드, 좋은 설계를 하고
www.inflearn.com
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard
코딩으로 학습하는 GoF의 디자인 패턴 강의 | 백기선 - 인프런
백기선 | 디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를
www.inflearn.com
서론
하나의 프로젝트를 위해 두명 혹은 그 이상 다수의 개발자가 투입된다. 보통 각각 맡은 영역 혹은 모듈 안에서 개발을 진행하지만 추가 수정사항이나 요건이 생길 경우, 혹은 다른 여러가지 이유들로 인하여 다른 사람이 개발한 기능을 수정하고 확장하는 경우는 항상 발생한다.
이때, 해당 코드를 읽고 의도를 파악한 후 추가적으로 개발을 진행함에 있어서 손쉽게 작업이 끝나는 경우가 있는 반면 수정범위를 특정하기 어렵고 사이드 이펙트가 발생하는 경우도 많다. 그리고 그 차이는 초기 코드 설계에서 비롯되는 경우가 많다.
적어도 내가 작성한 코드는 다른 사람이 수정하고 확장하기 좋은 코드로 작성되길 원했고, 그래서 요즘 객체 지향 이라던지 디자인 패턴에 대해 관심있게 공부하는 중이다. 그러던 중, 흔히 접했던 좋지 않은 케이스와 그에 대한 방안에 대해 고민해 볼 수 있는 시간이 생겨서 글을 작성하게 되었다.
흔히 접하는 케이스
(위에 기입된 강의들에서 제시한 예시를 좀 더 간략하게 수정하였습니다)
클라우드 서비스와 관련된 개발을 진행하는 상황이라고 가정하자.
현재는 DropBox와 Box의 서비스를 제공하고 있고, 각각의 클라우드 서비스는 파일 업로드와 파일 다운로드 기능을 제공한다고 하자.
아래는 해당 요건을 구현한 코드이다.
import java.util.List;
// 클라우드 서비스 종류를 나타내는 enum
enum CloudType {
DROPBOX,
BOX
}
// 메인 클래스
public class CloudFileManager {
private final CloudType cloudType;
public CloudFileManager(CloudType cloudType) {
this.cloudType = cloudType;
}
public void uploadFile(String fileName) {
if (cloudType == CloudType.DROPBOX) {
System.out.println("Dropbox: Uploading file: " + fileName);
} else if (cloudType == CloudType.BOX) {
System.out.println("Box: Uploading file: " + fileName);
}
}
public void downloadFile(String fileName) {
if (cloudType == CloudType.DROPBOX) {
System.out.println("Dropbox: Downloading file: " + fileName);
} else if (cloudType == CloudType.BOX) {
System.out.println("Box: Downloading file: " + fileName);
}
}
public static void main(String[] args) {
// 드롭박스 파일 관리
CloudFileManager dropboxManager = new CloudFileManager(CloudType.DROPBOX);
dropboxManager.uploadFile("test.txt");
dropboxManager.downloadFile("document.pdf");
System.out.println();
// 박스 파일 관리
CloudFileManager boxManager = new CloudFileManager(CloudType.BOX);
boxManager.uploadFile("report.docx");
boxManager.downloadFile("presentation.pptx");
}
}
그런데 추가적인 클라우드 서비스를 제공하게 되어 'NewBox' 가 추가된다면 어떻게 될까? 위 코드와 같은 구조라면 다음과 같이 코드를 추가해야 할 것이다
import java.util.List;
// 클라우드 서비스 종류를 나타내는 enum
enum CloudType {
DROPBOX,
BOX,
NEWBOX
}
// 메인 클래스
public class CloudFileManager {
private final CloudType cloudType;
public CloudFileManager(CloudType cloudType) {
this.cloudType = cloudType;
}
public void uploadFile(String fileName) {
if (cloudType == CloudType.DROPBOX) {
System.out.println("Dropbox: Uploading file: " + fileName);
} else if (cloudType == CloudType.BOX) {
System.out.println("Box: Uploading file: " + fileName);
} else if (cloudType == CloudType.NEWBOX) {
System.out.println("NewBox: Uploading file: " + fileName);
}
}
public void downloadFile(String fileName) {
if (cloudType == CloudType.DROPBOX) {
System.out.println("Dropbox: Downloading file: " + fileName);
} else if (cloudType == CloudType.BOX) {
System.out.println("Box: Downloading file: " + fileName);
} else if (cloudType == CloudType.NEWBOX) {
System.out.println("NewBox: Downloading file: " + fileName);
}
}
public static void main(String[] args) {
// 드롭박스 파일 관리
CloudFileManager dropboxManager = new CloudFileManager(CloudType.DROPBOX);
dropboxManager.uploadFile("test.txt");
dropboxManager.downloadFile("document.pdf");
System.out.println();
// 박스 파일 관리
CloudFileManager boxManager = new CloudFileManager(CloudType.BOX);
boxManager.uploadFile("report.docx");
boxManager.downloadFile("presentation.pptx");
System.out.println();
// new박스 파일 관리
CloudFileManager boxManager = new CloudFileManager(CloudType.NEWBOX);
boxManager.uploadFile("car.docx");
boxManager.downloadFile("report.pptx");
}
}
개선방향 : 팩토리 메서드 활용
여기에 'NewBox2, NewBox3 ...' 이 계속 추가된다면? 계속해서 'if else' 구문이 추가될 것이다. 기존 코드를 계속 수정해야되는 것이다. 이와 비슷한 코드들을 실무에서 종종 볼 수 있었는데, 공통된 기능을 추상화 하고 팩토리 메서드를 활용하여 동적으로 객체를 생성하는 방식을 적용한다면 확장성을 넓힐 수 있다.
수정한 코드는 아래와 같다.
// 공통 인터페이스: 모든 클라우드 서비스에서 동일한 메서드를 제공
interface CloudManager {
void uploadFile(String fileName);
void downloadFile(String fileName);
}
// 드롭박스 구현 클래스
class DropboxManager implements CloudManager {
@Override
public void uploadFile(String fileName) {
System.out.println("Dropbox: Uploading file: " + fileName);
}
@Override
public void downloadFile(String fileName) {
System.out.println("Dropbox: Downloading file: " + fileName);
}
}
// 박스 구현 클래스
class BoxManager implements CloudManager {
@Override
public void uploadFile(String fileName) {
System.out.println("Box: Uploading file: " + fileName);
}
@Override
public void downloadFile(String fileName) {
System.out.println("Box: Downloading file: " + fileName);
}
}
// 클라우드 서비스 타입 Enum
enum CloudType {
DROPBOX(DropboxManager.class),
BOX(BoxManager.class);
private final Class<? extends CloudManager> managerClass;
CloudType(Class<? extends CloudManager> managerClass) {
this.managerClass = managerClass;
}
public CloudManager createInstance() {
try {
return managerClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Failed to create CloudManager for: " + this.name(), e);
}
}
}
// 팩토리 메서드: Enum 기반 객체 생성
class CloudManagerFactory {
public static CloudManager getCloudManager(CloudType cloudType) {
return cloudType.createInstance();
}
}
// 통합 파일 관리 클래스: 클라우드 타입에 상관없이 일관된 인터페이스를 사용
class CloudFileManager {
private final CloudManager cloudManager;
// 생성자를 통해 사용할 클라우드 서비스를 주입
public CloudFileManager(CloudManager cloudManager) {
this.cloudManager = cloudManager;
}
public void uploadFile(String fileName) {
cloudManager.uploadFile(fileName);
}
public void downloadFile(String fileName) {
cloudManager.downloadFile(fileName);
}
}
// 메인 클래스: 클라우드 파일 관리 시스템 실행
public class Main {
public static void main(String[] args) {
// 드롭박스 파일 관리
CloudManager dropboxManager = CloudManagerFactory.getCloudManager(CloudType.DROPBOX);
CloudFileManager dropboxFileManager = new CloudFileManager(dropboxManager);
dropboxFileManager.uploadFile("test.txt");
dropboxFileManager.downloadFile("document.pdf");
System.out.println();
// 박스 파일 관리
CloudManager boxManager = CloudManagerFactory.getCloudManager(CloudType.BOX);
CloudFileManager boxFileManager = new CloudFileManager(boxManager);
boxFileManager.uploadFile("report.docx");
boxFileManager.downloadFile("presentation.pptx");
}
}
우선 클라우드에서 제공하는 공통 기능을 인터페이스에 추상화를 하였다. 그렇기 때문에 나중에 추가적인 클라우드기능을 제공하게 되더라도 인터페이스를 상속(구현)받아 세부 기능을 구현한 뒤, 클라우드 서비스 타입 enum 클래스에 추가된 클래스 필드를 추가하면 된다.
그리고 객체를 생성할 때는 enum에 추가된 필드를 이용하여 동적으로 객체를 생성할 수 있게 하였다. 이렇게 한다면 새로운 클라우드서비스가 추가될 때에도 단지 인터페이스를 상속받은 새로운 구현 클래스를 추가만 하면 된다.
이러한 방식처럼 기능을 추상화 하여 객체지향적인 코드를 작성하려고 고민한다면 어제보다 나은 코드를 작성할 수 있을 것이라고 생각한다.
'Java' 카테고리의 다른 글
Java 람다와 effectively final (0) | 2025.06.23 |
---|---|
@SchedulingConfigurer를 이용한 배치 시스템의 운영화 (0) | 2025.05.07 |
레이스 컨디션 (Race Condition) -01. @Transactional, synchronized (0) | 2024.04.09 |
어노테이션(Annotaion) + Custom Annotation (0) | 2024.04.01 |
리플렉션(Reflection) (0) | 2024.03.14 |