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

11,407 views

Published on

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

Published in: Technology
1 Comment
36 Likes
Statistics
Notes
No Downloads
Views
Total views
11,407
On SlideShare
0
From Embeds
0
Number of Embeds
6,587
Actions
Shares
0
Downloads
219
Comments
1
Likes
36
Embeds 0
No embeds

No notes for slide

객체 지향 발담그기 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)

×