design pattern #11 Structural pattern - Proxy

  • API Gateway  느낌의 proxy 가 아니고, 말 그대로 객체(database 연결)  등을 대리하는 proxy pattern 이다.
  • database connection 같은 경우, 무거운 resource 인데, 필요할때 쓰는 lazy initialization 이 좋은데, 이렇게 하면 connection 을 만들어 쓰는 곳마다 lazy initialization 을 해야한다.
  • original service object 와 동일한 proxy 를 만들어, 우리에게 필요한 추가 작업을 해서 필요한 경우에만 실제 object 를 실행하도록 할 수 있다.
  • lazy initialize, 결과를 caching 해두고 쓰거나, access control 을 할 수 있다는 장점이 있다.
  • 클래스 구조가 복잡해지거나,  한번 거쳐가기 때문에 응답에 delay 가 생긴다는 단점이 있다.
  • 코드를 3rd-party  라 바꿀수는 없는데, 추가 기능은 추가해야하는 경우 유용하다.

  • youtube downloader 예제로 보면,
  • 일단 ThirdPartYoutubeLib 가 interface 로 있고, ThirdPartyYoutubeClass 가 이를 구현한다.
  • YoutubeCacheProxy 도 ThirdPartyYoutubeLib 를 구현하는데, ThirdPartyYoutubeLib 를 필드로 갖는다.
  • cache 체크등을 하여 이미 있는 비디오는 cache 에서 리턴하는 등의 예제를 보여준다.
  • 일단 design pattern 을 보면, 모든것은 interface 로부터 시작하는것 같다. 그리고 그를 implement 하고, interface 를 field 로 갖고, 혹은, method 의  return type 이 interface 라거나.  등등이다. 

public interface ThirdPartyYouTubeLib {
HashMap<String, Video> popularVideos();
Video getVideo(String videoId);
}

public class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib {

@Override
public HashMap<String, Video> popularVideos() {
connectToServer("http://www.youtube.com");
return getRandomVideos();
}

@Override
public Video getVideo(String videoId) {
connectToServer("http://www.youtube.com/" + videoId);
return getSomeVideo(videoId);
}

// -----------------------------------------------------------------------
// Fake methods to simulate network activity. They as slow as a real life.

private int random(int min, int max) {
return min + (int) (Math.random() * ((max - min) + 1));
}

private void experienceNetworkLatency() {
int randomLatency = random(5, 10);
for (int i = 0; i < randomLatency; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}

private void connectToServer(String server) {
System.out.print("Connecting to " + server + "... ");
experienceNetworkLatency();
System.out.print("Connected!" + "\n");
}

private HashMap<String, Video> getRandomVideos() {
System.out.print("Downloading populars... ");

experienceNetworkLatency();
HashMap<String, Video> hmap = new HashMap<String, Video>();
hmap.put("catzzzzzzzzz", new Video("sadgahasgdas", "Catzzzz.avi"));
hmap.put("mkafksangasj", new Video("mkafksangasj", "Dog play with ball.mp4"));
hmap.put("dancesvideoo", new Video("asdfas3ffasd", "Dancing video.mpq"));
hmap.put("dlsdk5jfslaf", new Video("dlsdk5jfslaf", "Barcelona vs RealM.mov"));
hmap.put("3sdfgsd1j333", new Video("3sdfgsd1j333", "Programing lesson#1.avi"));

System.out.print("Done!" + "\n");
return hmap;
}

private Video getSomeVideo(String videoId) {
System.out.print("Downloading video... ");

experienceNetworkLatency();
Video video = new Video(videoId, "Some video title");

System.out.print("Done!" + "\n");
return video;
}

}
public class Video {
public String id;
public String title;
public String data;

Video(String id, String title) {
this.id = id;
this.title = title;
this.data = "Random video.";
}
}
public class YouTubeCacheProxy implements ThirdPartyYouTubeLib {
private ThirdPartyYouTubeLib youtubeService;
private HashMap<String, Video> cachePopular = new HashMap<String, Video>();
private HashMap<String, Video> cacheAll = new HashMap<String, Video>();

public YouTubeCacheProxy() {
this.youtubeService = new ThirdPartyYouTubeClass();
}

@Override
public HashMap<String, Video> popularVideos() {
if (cachePopular.isEmpty()) {
cachePopular = youtubeService.popularVideos();
} else {
System.out.println("Retrieved list from cache.");
}
return cachePopular;
}

@Override
public Video getVideo(String videoId) {
Video video = cacheAll.get(videoId);
if (video == null) {
video = youtubeService.getVideo(videoId);
cacheAll.put(videoId, video);
} else {
System.out.println("Retrieved video '" + videoId + "' from cache.");
}
return video;
}

public void reset() {
cachePopular.clear();
cacheAll.clear();
}
}
public class YouTubeDownloader {
private ThirdPartyYouTubeLib api;

public YouTubeDownloader(ThirdPartyYouTubeLib api) {
this.api = api;
}

public void renderVideoPage(String videoId) {
Video video = api.getVideo(videoId);
System.out.println("\n-------------------------------");
System.out.println("Video page (imagine fancy HTML)");
System.out.println("ID: " + video.id);
System.out.println("Title: " + video.title);
System.out.println("Video: " + video.data);
System.out.println("-------------------------------\n");
}

public void renderPopularVideos() {
HashMap<String, Video> list = api.popularVideos();
System.out.println("\n-------------------------------");
System.out.println("Most popular videos on YouTube (imagine fancy HTML)");
for (Video video : list.values()) {
System.out.println("ID: " + video.id + " / Title: " + video.title);
}
System.out.println("-------------------------------\n");
}
}
public class Demo {

public static void main(String[] args) {
YouTubeDownloader naiveDownloader = new YouTubeDownloader(new ThirdPartyYouTubeClass());
YouTubeDownloader smartDownloader = new YouTubeDownloader(new YouTubeCacheProxy());

long naive = test(naiveDownloader);
long smart = test(smartDownloader);
System.out.print("Time saved by caching proxy: " + (naive - smart) + "ms");

}

private static long test(YouTubeDownloader downloader) {
long startTime = System.currentTimeMillis();

// User behavior in our app:
downloader.renderPopularVideos();
downloader.renderVideoPage("catzzzzzzzzz");
downloader.renderPopularVideos();
downloader.renderVideoPage("dancesvideoo");
// Users might visit the same page quite often.
downloader.renderVideoPage("catzzzzzzzzz");
downloader.renderVideoPage("someothervid");

long estimatedTime = System.currentTimeMillis() - startTime;
System.out.print("Time elapsed: " + estimatedTime + "ms\n");
return estimatedTime;
}
}

Comments

Popular posts from this blog

삼성전자 무선사업부 퇴사 후기

개발자 커리어로 해외 취업, 독일 이직 프로세스

코드리뷰에 대하여