[객체 지향 프로그래밍 입문] 5. 추상화 예시

출처: 최범균님의 객체 지향 프로그래밍 입문 강의
https://www.inflearn.com/course/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%9E%85%EB%AC%B8/dashboard

기능 예시

  • 클라우드 파일 통합 관리 기능 개발
  • 대상 클라우드
    • 드롭박스, 박스
  • 주요 기능
    • 각 클라우드의 파일 목록 조회, 다운로드, 업로드, 삭제, 검색

추상화하지 않은 구현

  • 파일 목록 조회
public enum CloudId {
	DROPBOX,
	BOX;
}
public class FileInfo {
	private CloudId cloudId;
	private String fileId;
	private String name;
	private long length;

	... // get 메서드
}
// 1, 2: 비슷한 부분들
public class CloudFileManager {
	public List<FileInfo> getFileInfos(CloudId cloudId) {
		if (cloudId == CloudId.DROPBOX) { // 1
			// DropboxClient를 이용해서 파일 목록을 가져오고, 이걸 FileInfo로 변환
			DropboxClient dc = ...; //
			List<DbFile> dbFiles = db.getFiles(); // 2
			List<FileInfo> result = new ArrayList<>();

			for (DbFile dbFile : dbFiles) {
				FileInfo fi = new FileInfo();
				fi.setCloudId(CloutId.DROPBOX); // 2
				fi.setFileId(fi.getFileId()); // 2
				...
				result.add(fi);
			}
			result.add(fi);
		} else if (cloutId == CloudId.BOX) { // 1
			BoxService boxSvc = ...;
			...
		}
	}
}
  • 파일 다운로드
// 1, 2: 비슷한 부분들
public void download(FileInfo file, File localTarget) {
	if (file.getCloudId() == CloudId.DROPBOX) { // 1
		DropboxClient dc = ...; // 2
		FileOutputStream out = new FileOutputStream(localTarget);
		dc.copy(file.getFileId(), out); // 2
		out.close();
	} else if (file.getCloudId() == CloudId.BOX) { // 1
		BoxService boxSvc = ...; // 2
		InputStream is = boxScv.getInputStream(file.getId()); // 2
		FileOutputStream out = new FileOutputStream(localTarget);
		CopyUtil.copy(is, out); // 2
	}
}
  • 기타 기능 추가
public FileInfo upload(File file, CloudId cid) {
	if (cid == CloudId.DROPBOX) {
		...
	} else if (cid == CloudId.BOX) {
		...
	}
}
public void delete(File file, CloudId cid) {
	if (cid == CloudId.DROPBOX) {
		...
	} else if (cid == CloudId.BOX) {
		...
	}
}
public List<FileInfo> search(String query, CloudId cid) {
	if (cid == CloudId.DROPBOX) {
		...
	} else if (cid == CloudId.BOX) {
		...
	}
}
  • 이어지는 추가
    • 클라우드 추가 (S클라우드, N클라우드, D클라우드)
    • 기능 추가 (클라우드 간 파일 복사)
  • 클라우드 추가
public class CloudFileManager {
	public List<FileInfo> getFileInfos(CloudId cloudId) {
		if (cloudId == CloudId.DROPBOX) {
			...
		} else if (cloutId == CloudId.BOX) {
			...
		} else if (cloutId == CloudId.SCLOUD) { // 추가
			...
		} else if (cloutId == CloudId.NCLOUD) { // 추가
			...
		} else if (cloutId == CloudId.DCLOUD) { // 추가
			...
		}
	}
}

download(), upload(), delete(), search()도 유사한 else-if 블록 추가

  • 클라우드 간 복사
public FileInfo copy(FileInfo fileInfo, CloudId to) {
	CloudId from = fileInfo.getCloudId(); 
	if (to = CloudId.DROPBOX) {
		DropBoxClient dbClient = ...; 
	if (from =CloudId.BOX) { // 1
		dbClient.copyFromUrl("http://www.box.com/files/"+fileInfo.getFileId()); 
	} else if (from = CloudId.SCLOUD) { // 1
		ScloudClient sClient = ...; 
		InputStream is = sClient.getInputStream(fileInfo.getFileId());
		dbClient.copyFromInputStream(is, fileInfo.getName()); 
	} else if (from ==CloudId.DCLOUD) { // 1
		dbClient.copyFromUrl("http://www.dcloud.com/getfile?fileId=+fileInfo.getFileId()); 
	} else if (from == CloudId.NCLOUD) { // 1
		NCloudClient nClient = ...;
		nClient.Save(fileInfo.getFileId(), temp); 
		InputStream is = new FileInputStream(temp); 
		dbClient.copyFromInputStream(is, fileInfo.getName());
	}
}

if-else만 작성했는데도 복잡

추상화

  • 추상화해보면
DROPBOX, BOX, SCLOUD, DCLOUD, NLCOUD => 클라우드 파일 시스템
  • 클라우드 파일 시스템 설계

  • DROPBOX 구현
public class DropBoxFileSystem implements CloudFileSystem { // CloudFileSystem을 상속
	private DropBoxClient dbClient = new DropBoxClient(...);

	@Override public List<CloudFile> getFiles() {
		// 내부적으로는 DropBoxClient를 이용하여 구현하지만 실제 결과물은 CloudFile을 리턴하는 식으로 구현
		List<DbFile> dbFiles = dbClient.getFiles(); 
		List<CloudFile> results = new ArrayList<>(dbFiles.size()); 
		for (DbFile file : dbFiles) {
			DropBoxCloudFile cf = new DropBoxCloudFile(file, dbClient); 
			results.add(cf);
		}
		return results;
	}
}
public class DropBoxCloudFile implements CloudFile {
	private DropBoxClient dbClient; 
	private DbFile dbFile;

	public DropBoxCloudFile(DbFile dbFile, dbClient) {
		this.dbFile = dbFile; 
		this.dbClient = dbClient;
	}
	public String getId() {
		return dbFile.getId();
	}
	public boolean hasUrl() {
		return true;
	}
	public String getUrl() {
		return dbFile.getFileUrl();
	}
	public String getName() {
		return dbFile.getFileName();
	}
	public InputStream getInputStream() { 
		return dbClient.createStreamOfFile(dbFile);
	}
	public void write(OutputStream out) {
		...
	}
	public void delete() {
		dbClient.deleteFile(dbFile.getId());
	}
}
  • 파일 목록, 다운로드 기능 구현
public List<CloudFile> getFileInfos(CloudId cloudId) { 
	CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(cloudId); 
	return fileSystem.getFiles();
}

public void download (CloudFile file, File localTarget) {
	file.write(new FileOutputStream(localTarget));
}
  • BOX 클라우드 지원 추가

Concrete 클래스를 구현하면 box 클라우드 지원 가능

그리고 CloudFileSystemFactory에서 CloudFileId에 따라서 알맞게 box file 시스템을 리턴하도록 하면 됨

  • 파일 목록, 다운로드 기능 구현 (BOX 지원 추가 이후)
public List<CloudFile> getFileInfos(CloudId cloudId) { 
	CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(cloudId); 
	return fileSystem.getFiles();
}

public void download(CloudFile file, File localTarget) {
	file.write(new FileOutputStream(localTarget));
}
  • 파일 복사 기능
public void copy(CloudFile file, CloudId target) { 
	CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(target); 
	fileSystem.copyFrom(file);
}
-- DropBoxFileSystem 
private DropBoxClient dbClient = new DropBoxClient(...); 
public void copyFrom(CloudFile file) { 
	if (file.hasUrl())
		dbClient.copyFromUrl(file.getUrl()); 
	else
		dbClient.copyFromInputStream(file.getInputStream(), file.getName());
}
-- NcloudFileSystem 
private NcloudClient nClient = new NCloudClient(...); 
public void copyFrom(CloudFile file) {
	File tempFile = File.createTemp(); 
	file.write(new FileOutputStream(tempFile)); 
	nClient.upload(tempFile, file.getName());
}
  • 추상화 결과

그리고 이 코드는 특정 클라우드 관련 코드가 한 곳에 모인다는 장점도 있음

이것이 바로 OCP(Open-Closed Principle, 개방-폐쇄 원칙)

기능을 변경하거나 확장에는 열려 있으면서 그 기능을 사용하는 코드는 수정하지 않아야 한다.

좋은 웹페이지 즐겨찾기