OOP / / 2024. 3. 9. 15:30

개방 폐쇄 원칙(Open-Close Principle, OCP)

설명

  • 확장에는 열려있고 수정에는 닫혀있다는 원칙입니다.
  • 새로운 기능이나 요구사항이 추가되거나 변경되더라도 기존의 코드를 변경하지 않고도 기능을 확장 할 수 있어야 한다는 원칙입니다.
  • 주로 추상화와 다형성의 성질을 이용하여 구현합니다.
  • 실제 코드에서 추상화된 인터페이스를 기반으로 기능을 호출하고 알맞게 의존성 주입을 받은 구현체의 세부로직을 실행하여 새로운 기능을 추가하더라도 기존 코드에 영향을 주지 않도록 합니다.
  • OCP 원칙을 따르는 설계는 시스템의 유지보수성의 향상을 가져옵니다. 새로운 기능이나 요구사항의 추가가 기존 코드의 변경을 필요로 하지 않습니다. 그리고 하나의 추상화된 개념으로 여러개의 구현체를 만들도록하여 재사용성을 향상 시킵니다.

 

Bad Case

public class Sample {

    public void createFile(UploadType type, String path, byte[] contents) {
        if (type.equals(UploadType.LOCAL)) {
            LocalFileUploader uploader = new LocalFileUploader();
            uploader.create(path, contents);
            uploader.delete(path);
        } else if (type.equals(UploadType.NFS)) {
            NfsFileUploader uploader = new NfsFileUploader();
            uploader.create(path, contents);
            uploader.delete(path);
        } else if (type.equals(UploadType.SFTP)) {
            SftpFileUploader uploader = new SftpFileUploader();
            uploader.create(path, contents);
            uploader.delete(path);
        } else {
            throw new RuntimeException("not found");
        }
    }
}

 

Good Case

public interface FileUploader {
    void createFile(String path, byte[] contents);
}

public class FileUploaderManager {

    private final Map<UploadType, FileUploader> map = new HashMap<>();

    public FileUploaderManager() {

    }

    public void addUploader(UploadType type, FileUploader uploader) {
        this.map.put(type, uploader);
    }

    public void createFile(UploadType type, String path, byte[] contents) {
        FileUploader uploader = this.map.get(type);

        if(uploader == null) {
            throw new RuntimeException("not found");
        }

        uploader.createFile(path, contents);
    }
}

public class Sample {
    public static void main(String[] args) {
        FileUploaderManager manager = new FileUploaderManager();
        String path = ""; //임의의 데이터
        byte[] contents = {}; //임의의 데이터

        manager.addUploader(UploadType.LOCAL, new LocalFileUploader());
        manager.addUploader(UploadType.NFS, new NfsFileUploader());
        manager.addUploader(UploadType.SFTP, new SftpFileUploader());

        manager.createFile(UploadType.LOCAL, path, contents);
    }
}

 

Why?

createFile 함수는 파일 생성과 삭제라는 중요한 작업을 수행하는데, 이 과정에서 업로더의 유형을 기반으로 적절한 업로더 객체를 선택하고 사용합니다.

Bad Case의 시나리오에서는 새로운 업로더 유형이 도입될 때마다 createFile 함수 자체를 수정해야 합니다. 이는 새로운 요구 사항이나 기능의 추가가 기존 코드의 변경을 필요로 하게 만듭니다, 이는 개방-폐쇄 원칙(OCP)의 기본 아이디어, 즉 변경에는 닫혀 있어야 한다는 원칙에 반합니다.

반면에, Good Case에서는 새로운 업로더 유형이 추가되어도, 해당 유형의 FileUploader 인터페이스 구현체만 새로 등록하면 됩니다. 이 방식은 확장에 열려 있도록 설계되었으며, createFile 함수의 기존 로직을 변경할 필요가 없습니다.

이로 인해, Good Case는 새로운 기능이나 요구 사항을 기존 코드에 영향을 주지 않고 통합할 수 있는 유연성을 제공합니다. 결과적으로, 시스템은 변경에 대한 필요성이 발생할 때마다 수정되는 것이 아니라, 새로운 기능을 쉽게 추가할 수 있는 구조를 갖추게 됩니다.

이런 접근 방식은 개방-폐쇄 원칙(OCP)을 실천함으로써 소프트웨어의 유지보수성을 높이고, 시간이 지남에 따라 발생할 수 있는 비용을 줄이는 데 기여합니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유