Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

객체 지향 발담그기 JCO 컨퍼런스 14회

14,331 views

Published on

2014년 JCO 컨퍼런스 14회, 객체 지향 발들이기 세션 발표 자료.

Published in: Technology

객체 지향 발담그기 JCO 컨퍼런스 14회

  1. 1. 객체 지향 발담그기 최범균, madvirus@madvirus.net
  2. 2. 오늘의 주제 추상화 + 유연함
  3. 3. 여러 클라우드에 올라간 파일들을 통합 관리하는 앱을 만들어보자. 출처: http://www.flickr.com/photos/42402605@N07/5693120074
  4. 4. 빨리 선보이기 위한 최초 0.1 버전 •  대상 클라우드 o  드롭박스, 박스 o  각 클라우드의 목록 조회하기 각 클라우드에서 파일 다운로드 하기 •  주요 기능 o 
  5. 5. 파일 정보 표현 public class FileInfo { private CloudId cloudId; private String name; private long length; … // get 메서드 } public enum CloudId { DROPBOX, BOX; }
  6. 6. 파일 목록 조회 public List<FileInfo> getFileInfos(CloudId cloudId) { if (cloudId == CloudId.DROPBOX) { DropboxClient dc = …; List<DbFile> dbFiles = db.getFiles(); List<FileInfo> result = new ArrayList<>(); for (DbFile dbFile : dbFiles) { FileInfo fi = new FileInfo(); fi.setCloudId(CloudId.DROPBOX); … result.add(fi); } return result; } else if (cloudId == CloudId.BOX) { BoxService boxSvc = …; … // } }
  7. 7. 파일 다운로드 (계속) public void download(FileInfo file, File localTarget) { if (file.getCloudId() == CloudId.DROPBOX) { DropboxClient dc = …; FileOutputStream out = new FileOutputStream(localTarget); dc.copy(file.getFileId(), out); out.close(); } else if (file.getCloudId() == CloudId.BOX) { // TODO } 동작하도록 반영 } public class FileInfo { private CloudId cloudId; private String fileId; private String name; ... for (DbFile dbFile : dbFiles) { FileInfo fi = new FileInfo(); fi.setFileId(dbFile.getId()); fi.setCloudId(cloudId); … }
  8. 8. 파일 다운로드 public void download(FileInfo file, File localTarget) { if (file.getCloudId() == CloudId.DROPBOX) { ... } else if (file.getCloudId() == CloudId.BOX) { BoxService boxSvc = …; InputStream is = boxSvc.getInputStream(file.getId()); FileOutputStream out = new FileOutputStream(localTarget); CopyUtil.copy(is, out); } }
  9. 9. 야호! •  무사히 동작하는 0.1 버전 완성 •  0.5 버전을 향해 o  기능 확대 §  파일 업로드 §  파일 삭제 §  검색 §  이미지 미리보기
  10. 10. 0.5v 기능 추가 public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... public void delete(String fileId, 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) { ... } }
  11. 11. 음... •  무사히 동작하는 0.5 버전 완성 •  베타 버전을 향해 o  o  클라우드 추가 §  S클라우드 §  D클라우드 §  N클라우드 §  ... 기능 추가 §  클라우드 간 파일 복사
  12. 12. 과정 public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } 코드복사 } public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.BOX) { ... } } public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.SCLOUD) { ScloudClient scClient = …; ... 구현변경 } } public FileInfo upload(File file, CloudId cid) { if (cid == CloudId.DROPBOX) { ... 조건변경 } else if (cid == CloudId.BOX) { ... } else if (cid == CloudId.SCLOUD) { ... } }
  13. 13. 흔한 실수 public List<FileInfo> getFileInfos(CloudId cloudId) { if (cloudId == CloudId.DROPBOX) { … } else if (cloudId == CloudId.BOX) { … } else if (cloudId == CloudId.SCLOUD) { … } else if (cloudId == CloudId.DCLOUD) { … public void delete(String fileId, CloudId cid) { if (cid == CloudId.DROPBOX) { ... return result; } else if (cloudId == CloudId.BOX) { … // } else if (cloudId == CloudId.SCLOUD) { … // alert(“파일이 존재하지 않습니다.”); ... } else if (cloudId == CloudId.DCLOUD) { … fi.setCloudId(CloudId.SCLOUD); … } else if (cloudId == CloudId.NCLOUD) { … } } 음.. 파일이 있는데, 왜 없다 그러지? alert(“파일이 존재하지 않습니다.”); ... } else if (cloudId == CloudId.NCLOUD) { … // } } OO;;
  14. 14. 클라우드 간 파일 복사 기능 고려사항 •  URL로부터 복사해서 가져오는 클라우드 존재 o  그런데, URL을 제공하지 않는 클라우드 존재 o  그런데, InputStream을 제공하지 않는 클라우드 존재 •  InputStream을 제공하면 되는 클라우드 존재 •  로컬 File을 제공해야 하는 클라우드 존재
  15. 15. 클라우드 간 파일 복사 기능 구현 ... public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { dbClient.copyFromUrl(“http://www.box.com/files/”+fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream(fileInfo.getFileId()); dbClient.copyFromInputStream(is, fileInfo.getName()); } else if (from ==CloudId.DCLOUD) { dbClient.copyFromUrl(“http://www.dcloud.com/getfile?fileId=”+fileInfo.getFileId()); } else if (from == CloudId.NCLOUD) { NCloudClient nClient = …; File temp = File.createTemp(); nClient.save(fileInfo.getFileId(), temp); InputStream is = new FileInputStream(temp); dbClient.copyFromInputStream(is, fileInfo.getName()); } } else if (to == CloudId.BOX) { // 코드 계속 ….
  16. 16. 클라우드 간 파일 복사 기능 구현 ... } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { boxService.createFileByUrl(“http://www.dropbox.com/box/files/”+fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream(fileInfo.getFileId()); boxService.uploadFile(is, fileInfo.getName()); } else if (from ==CloudId.DCLOUD) { boxService.createFileByUrl(“http://www.dcloud.com/getfile?fileId=”+fileInfo.getFileId()); } else if (from == CloudId.NCLOUD) { NCloudClient nClient = …; File temp = File.createTemp(); nClient.save(fileInfo.getFileId(), temp); boxService.uploadFile(temp, fileInfo.getName()); } } else if (to == CloudId.SCLOUD) { // 코드 계속 ….
  17. 17. 클라우드 간 파일 복사 기능 구현 ... } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.DCLOUD) { // 코드 계속 ….
  18. 18. 클라우드 간 파일 복사 기능 코드 (if-else 구조만 표시) public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.DCLOUD) { DcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.NCLOUD) { NcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.DCLOUD) { … ... } } } .
  19. 19. 새로운 요구사항 public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { // URL로 복사하기 지원 DropBoxClient dbClient = …; if (from == CloudId.BOX) { ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.BOX) { // URL로 복사하기 지원 BoxService boxService = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.SCLOUD) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.SCLOUD) { ScloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.DCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.DCLOUD) { DcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.NCLOUD) { … ... } } else if (to == CloudId.NCLOUD) { NcloudClient sClient = …; if (from == CloudId.DROPBOX) { … ... } else if (from == CloudId.BOX) { … ... } else if (from ==CloudId.SCLOUD) { … ... } else if (from == CloudId.DCLOUD) { … ... } } 클라우드 10개 추가 } .
  20. 20. 1개 클라우드 추가 비용 개발 비용 예전엔 빨리 했는 데…... 시간
  21. 21. 개발시간이 왜 증가하는가?
  22. 22. 개발 시간 증가 이유 •  코드 구조가 길어지고 복잡해짐 o  새로운 클라우드 추가시 모든 메서드에 새로운 if 블록 추가 §  중첩 if-else는 복잡도 배로 증가 §  if-else가 많을수록 진척 더딤 (신중 모드) •  관련 코드가 여러 곳에 분산됨 o  한 클라우드 처리와 관련된 코드가 여러 메서드에 흩어짐 o  코드 추가에 따른 노동 시간 증가 실수하기 쉽고, 이로 인한 불필요한 디버깅 시간 증가 •  결과적으로, 코드 가독성과 분석 속도 저하 o 
  23. 23. 해결책 데이터와기능을함께갖는 객체로추상화Abstraction
  24. 24. 추상화 DROPBOX BOX SCLOUD DCLOUD NCLOUD 클라우드 파일시스템
  25. 25. 클라우드 파일 시스템 설계
  26. 26. DROPBOX용 구현 (계속) public class DropBoxFileSystem implements CloudFileSystem { private DropBoxClient dbClient = new DropBoxClient(...); @Override public List<CloudFile> getFiles() { 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; }
  27. 27. DROPBOX용 구현 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()); } … }
  28. 28. v0.1의 파일 목록/다운로드 기능 구현 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)); } 현재 드롭박스 지원만 구현 된 상태
  29. 29. BOX 클라우드 지원 추가
  30. 30. v0.1의 파일 목록/다운로드 기능 구현 (다시) 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)); } 박스 지원도 반영 (코드 그대로네… 응?)
  31. 31. 복잡했던 파일 복사 기능의 변신: copyFrom(CloudFile file) 추가 public void copy(CloudFile file, CloudId target) { CloudFileSystem fileSystem = CloudFileSystemFactory.getFileSystem(target); fileSystem.copyFrom(file); }
  32. 32. 복잡했던 파일 복사 기능의 변신: 파일시스템 별 copyFrom 메서드 -- 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()); }
  33. 33. 추상화를 잘 했더니 public class CloudFileManeger { 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); } … ... } 추상화된 타입으로만 핵심 기능 구현 가능
  34. 34. 추상화를 잘 했더니 새로운 클라우드 지원을 추가 코드 수정없이
  35. 35. 이것이 바로 OCP Open-Closed principle Open for Extenstion 확장에 열려 있음 Closed for Modification 수정엔 닫혀 있음
  36. 36. 추상화를 잘 했더니: 기능이 만들어 짐 •  URL 제공 여부 및 구성 코드가 메서드로 이동 public FileInfo copy(FileInfo fileInfo, CloudId from, CloudId to) { if (to == CloudId.DROPBOX) { DropBoxClient dbClient = …; if (from == CloudId.BOX) { dbClient.copyFromUrl( “http://www.box.com/files/”+ fileInfo.getFileId()); } else if (from == CloudId.SCLOUD) { ScloudClient sClient = …; InputStream is = sClient.getInputStream( fileInfo.getFileId()); dbClient.copyFromInputStream(is, fileInfo.getName()); -- DropBoxFileSystem private DropBoxClient dbClient = ...; public void copyFrom(CloudFile file) { if (file.hasUrl()) dbClient.copyFromUrl(file.getUrl()); else dbClient.copyFromInputStream( file.getInputStream(), file.getName()); }
  37. 37. 이것이 바로 캡슐화Encapsulation •  내부 구현을 외부에 감춤 o  내부 구현을 변경해도 이를 사용하는 코드 영향 최소화 -- DropBoxFileSystem private DropBoxClient dbClient = ... URL 생성 규칙이 변경되어도 이 코드는 바뀌지 않음 public void copyFrom(CloudFile file) { if (file.hasUrl()) dbClient.copyFromUrl(file.getUrl()); else URL 제공 여부가 변경되어도 이 코드는 바뀌지 않음 dbClient.copyFromInputStream( file.getInputStream(), file.getName()); }
  38. 38. OCP/캡슐화가 적용된 코드의 1개 클라우드 추가 비용 개발 비용 그냥 막 작성한 코드 OCP/캡슐화 등 좋은 설계를 가진 코드 시간
  39. 39. 이것이 객체 지향을 해야하는 이유 비용 (즉, 변경의 유연함)
  40. 40. 추상화는 언제? •  유연함이 필요한 시점(변화가 생기는 시점) o  o  예, 1개 클라우드 지원에서 2개 클라우드로 확장 예, 물류업체 추가 •  아직 구현할 수 없는 부분이 존재할 때 o  예, 물류 업체와의 연동 프로토콜 미확정 §  물류 시스템 연동을 추상화해서 인터페이스로 표현 •  추상화(모델링)에는 비용이 발생 o  과도한 추상화(모델링)는 불필요한 복잡도를 증가시킴
  41. 41. 정리 •  개발 시간을 줄이기 위해 필요한 것 중 하나, o  변화를 수용할 수 있는 설계/구현 o  o  변화에 대한 유연함 제공 이는 개발 비용(즉, 시간) 절감 효과! o  캡슐화, 추상화, 다형성, SOLID o  좋은 코드, 패턴, 테스트 주도 개발, 리팩토링 등 •  잘 된 추상화/캡슐화가 해 주는 것 •  익혀야 할 것 → 객체 지향 •  더불어 익힐 것
  42. 42. 참고 서적
  43. 43. 감사합니다. 연락은 트위터(@madvirus) 또는 이메일로(madvirus@madvirus.net)

×