객체지향 디자인 패턴 1편

Singleton, Strategy, State, Command, Adapter, Proxy

객체지향 언어로 프로젝트를 진행할 때 익혀두면 유용할 패턴들.
영상에서 여섯가지를 패턴들을 알아보겠습니다.


패턴 분:초
Singleton 패턴 01:01
Strategy 패턴 03:52
State 패턴 06:48
Command 패턴 08:19
Adapter 패턴 11:09
Proxy 패턴 13:52

❗각 패턴의 내용들이 서로 연결된 부분이 있기 때문에
가능한 전체 영상을 시청할 것을 권장합니다.

예제 코드들을 아래에서 각각 확인하세요 😊


Singleton 패턴

프로세스에 특정 객체가 하나만 존재해야 할 때 쓰는 패턴이죠.
화면들에 공유되는 앱 세팅을 싱글턴으로 설정해보는 예제입니다.

MyProgram.java

package singleton.after; public class MyProgram { public static void main (String[] args) { new FirstPage().setAndPrintSettings(); new SecondPage().printSettings(); } }

Settings.java

🔍 getSettings 메소드를 잘 살펴보세요.

package singleton.after; public class Settings { private Settings () {}; private static Settings settings = null; public static Settings getSettings () { if (settings == null) { settings = new Settings(); } return settings; } private boolean darkMode = false; private int fontSize = 13; public boolean getDarkMode () { return darkMode; } public int getFontSize () { return fontSize; } public void setDarkMode (boolean _darkMode) { darkMode = _darkMode; } public void setFontSize (int _fontSize) { fontSize = _fontSize; } }

FirstPage.java

🔍 settings 변수에 어떻게 값이 들어가는지 주목하세요.

package singleton.after; public class FirstPage { private Settings settings = Settings.getSettings(); public void setAndPrintSettings () { settings.setDarkMode(true); settings.setFontSize(15); System.out.println(settings.getDarkMode() + " " + settings.getFontSize()); } }

SecondPage.java

package singleton.after; public class SecondPage { private Settings settings = Settings.getSettings(); public void printSettings () { System.out.println(settings.getDarkMode() + " " + settings.getFontSize()); } }

🌟 Mark Sunghun Park님께서 댓글로 보충해주신 내용입니다.

  • 자바에서 라이브러리나 프레임웤없이 가장 안전하게 싱글톤을 생성할 수 있는 방법 중의 하나는 inner static class singleton입니다.
public class InnerStaticSingleton implements Serializable { private InnerStaticSingleton() { } public static InnerStaticSingleton getInstance() { return InnerClass.instance; } private static class InnerClass { private static final InnerStaticSingleton instance = new InnerStaticSingleton(); } }
  • 이 방법을 사용하면 멀티쓰레드에 안전하고, Lazy loading을 지원하며, serialization에도 안전한 싱글톤을 만들 수 있습니다.

Strategy 패턴

Main.java

package strategy.after; public class Main { public static void main(String[] args) { MyProgram myProgram = new MyProgram(); myProgram.testProgram(); } }

MyProgram.java

package strategy.after; public class MyProgram { private SearchButton searchButton = new SearchButton(this); public void setModeAll () { searchButton.setSearchStrategy(new SearchStratagyAll()); } public void setModeImage () { searchButton.setSearchStrategy(new SearchStratagyImage()); } public void setModeNews () { searchButton.setSearchStrategy(new SearchStratagyNews()); } public void setModeMap () { searchButton.setSearchStrategy(new SearchStratagyMap()); } public void testProgram () { searchButton.onClick(); // "SEARCH ALL" 출력 setModeImage(); // 이미지검색 모드로 searchButton.onClick(); // "SEARCH IMAGE" 출력 setModeNews(); // 뉴스검색 모드로 searchButton.onClick(); // "SEARCH NEWS" 출력 setModeMap(); // 지도검색 모드로 searchButton.onClick(); // "SEARCH MAP" 출력 } }

SearchStrategy.java

package strategy.after; interface SearchStrategy { public void search (); } class SearchStratagyAll implements SearchStrategy { public void search () { System.out.println("SEARCH ALL"); // 전체검색하는 코드 } } class SearchStratagyImage implements SearchStrategy { public void search () { System.out.println("SEARCH IMAGE"); // 이미지검색하는 코드 } } class SearchStratagyNews implements SearchStrategy { public void search () { System.out.println("SEARCH NEWS"); // 뉴스검색하는 코드 } } class SearchStratagyMap implements SearchStrategy { public void search () { System.out.println("SEARCH MAP"); // 지도검색하는 코드 } }

SearchButton.java

package strategy.after; public class SearchButton { private MyProgram myProgram; public SearchButton (MyProgram _myProgram) { myProgram = _myProgram; } private SearchStrategy searchStrategy = new SearchStratagyAll(); public void setSearchStrategy (SearchStrategy _searchStrategy) { searchStrategy = _searchStrategy; } public void onClick () { searchStrategy.search(); } }

State 패턴

MyProgram.java

package state; public class MyProgram { public static void main(final String[] args) { final ModeSwitch modeSwitch = new ModeSwitch(); modeSwitch.onSwitch(); // "FROM LIGHT TO DARK" 출력 modeSwitch.onSwitch(); // "FROM DARK TO LIGHT" 출력 modeSwitch.onSwitch(); // "FROM LIGHT TO DARK" 출력 modeSwitch.onSwitch(); // "FROM DARK TO LIGHT" 출력 } }

ModeState.java

package state; public interface ModeState { public void toggle (ModeSwitch modeSwitch); } class ModeStateLight implements ModeState { public void toggle (ModeSwitch modeSwitch) { System.out.println("FROM LIGHT TO DARK"); // 화면을 어둡게 하는 코드 // .. // .. modeSwitch.setState(new ModeStateDark()); } } class ModeStateDark implements ModeState { public void toggle (ModeSwitch modeSwitch) { System.out.println("FROM DARK TO LIGHT"); // 화면을 밝게 하는 코드 // .. // .. modeSwitch.setState(new ModeStateLight()); } }

ModeStitch.java

package state; public class ModeSwitch { private ModeState modeState = new ModeStateLight(); public void setState (ModeState _modeState) { modeState = _modeState; } public void onSwitch () { modeState.toggle(this); } }

Command 패턴

MyProgram.java

package command; public class MyProgram { public static void main(String[] args) { RobotKit robotKit = new RobotKit(); robotKit.addCommand(new MoveForwardCommand(2)); robotKit.addCommand(new TurnCommand(Robot.Direction.LEFT)); robotKit.addCommand(new MoveForwardCommand(1)); robotKit.addCommand(new TurnCommand(Robot.Direction.RIGHT)); robotKit.addCommand(new PickupCommand()); robotKit.start(); // 2 칸 전진 // 왼쪽으로 방향전환 // 1 칸 전진 // 오른쪽으로 방향전환 // 앞의 물건 집어들기 } }

Robot.java

package command; public class Robot { public enum Direction { LEFT, RIGHT } public void moveForward (int space) { System.out.println(space + " 칸 전진"); } public void turn (Direction _direction) { System.out.println( (_direction == Direction.LEFT ? "왼쪽" : "오른쪽") + "으로 방향전환" ); } public void pickup () { System.out.println("앞의 물건 집어들기"); } }

Command.java

package command; abstract class Command { protected Robot robot; public void setRobot (Robot _robot) { this.robot = _robot; } public abstract void execute (); } class MoveForwardCommand extends Command { int space; public MoveForwardCommand (int _space) { space = _space; } public void execute () { robot.moveForward(space); } } class TurnCommand extends Command { Robot.Direction direction; public TurnCommand (Robot.Direction _direction) { direction = _direction; } public void execute () { robot.turn(direction); } } class PickupCommand extends Command { public void execute () { robot.pickup(); } }

RobotKit.java

package command; import java.util.ArrayList; public class RobotKit { private Robot robot = new Robot(); private ArrayList<Command> commands = new ArrayList<Command>(); public void addCommand (Command command) { commands.add(command); } public void start () { for (Command command : commands) { command.setRobot(robot); command.execute(); } } }

Adapter 패턴

Strategy 패턴에 적용한 코드

FindAlgorithm.java

package adapter.search; interface FindAlgorithm { public void find (boolean global); } class FindMovieAlgorithm implements FindAlgorithm { public void find (boolean global) { System.out.println( "find movie" + (global ? " globally" : "") ); // 동영상검색하는 코드 // ... // ... } }

SearchStrategy.java에 추가된 부분

//... class SearchFindAdapter implements SearchStrategy { private FindAlgorithm findAlgorithm; public SearchFindAdapter (FindAlgorithm _findAlgorithm) { findAlgorithm = _findAlgorithm; } public void search () { findAlgorithm.find(true); } }

MyProgram.에 추가된 부분

//... public void setModeMovie () { searchButton.setSearchStrategy( new SearchFindAdapter(new FindMovieAltorithm()) ); }

Command 패턴에 적용한 코드

Order.java

package adapter.robot; interface Order { public void run (Robot robot); } class MoveBackOrder implements Order { private int block; public MoveBackOrder(int _block) { block = _block; } public void run (Robot robot) { robot.turn(Robot.Direction.LEFT); robot.turn(Robot.Direction.LEFT); robot.moveForward(block); } }

Command.java에 추가된 부분

class CommandOrderAdapter extends Command { private Order order; public CommandOrderAdapter (Order _order) { order = _order; } public void execute () { order.run(robot); } }

MyProgram.java에 추가된 부분

class CommandOrderAdapter extends Command { private Order order; public CommandOrderAdapter (Order _order) { order = _order; } public void execute () { order.run(robot); } }

Proxy 패턴

MyProgram.java

package proxy; import java.util.ArrayList; public class MyProgram { public static void main(String[] args) { ArrayList<Thumbnail> thumbnails = new ArrayList<Thumbnail>(); thumbnails.add(new ProxyThumbnail("Git 강좌", "/git.mp4")); thumbnails.add(new ProxyThumbnail("Rest API란?", "/rest-api.mp4")); thumbnails.add(new ProxyThumbnail("도커 사용법", "/docker.mp4")); thumbnails.add(new ProxyThumbnail("객체지향 프로그래밍", "/oodp.mp4")); thumbnails.add(new ProxyThumbnail("블록체인의 원리", "/blockchain.mp4")); for (Thumbnail thumbnail : thumbnails) { thumbnail.showTitle(); } // 제목:Git 강좌 // 제목:Rest API란? // 제목:도커 사용법 // 제목:객체지향 프로그래밍 // 제목:블록체인의 원리 thumbnails.get(2).showPreview(); thumbnails.get(2).showPreview(); thumbnails.get(4).showPreview(); thumbnails.get(1).showPreview(); // /docker.mp4로부터 도커 사용법의 영상 데이터 다운 // 도커 사용법의 프리뷰 재생 // 도커 사용법의 프리뷰 재생 // /blockchain.mp4로부터 블록체인의 원리의 영상 데이터 다운 // 블록체인의 원리의 프리뷰 재생 // /rest-api.mp4로부터 Rest API란?의 영상 데이터 다운 // Rest API란?의 프리뷰 재생 } }

Thumbnail.java

package proxy; interface Thumbnail { public void showTitle (); public void showPreview (); } class RealThumbnail implements Thumbnail { private String title; private String movieUrl; public RealThumbnail (String _title, String _movieUrl) { title = _title; movieUrl = _movieUrl; // URL로부터 영상을 다운받는 작업 - 시간 소모 System.out.println(movieUrl + "로부터 " + title + "의 영상 데이터 다운"); } public void showTitle () { System.out.println("제목:" + title); } public void showPreview () { System.out.println(title + "의 프리뷰 재생"); } } class ProxyThumbnail implements Thumbnail { private String title; private String movieUrl; private RealThumbnail realThumbnail; public ProxyThumbnail (String _title, String _movieUrl) { title = _title; movieUrl = _movieUrl; } public void showTitle () { System.out.println("제목:" + title); } public void showPreview () { if (realThumbnail == null) { realThumbnail = new RealThumbnail(title, movieUrl); } realThumbnail.showPreview(); } }

🍿 더 자세한 내용은 영상에서 보실 수 있습니다.





관련 태그의 다른 영상들

객체지향 디자인 패턴 2
Facade, Template-method, Decorator, Factory, Abstract-factory Mediator, Composite 패턴들을 알아봅니다.
# 객체지향
# 디자인패턴
# 상속
# interface
# 객체
# super
객체지향 디자인 패턴 1
Singleton, Strategy, State, Command, Adapter, Proxy 패턴들을 알아봅니다.
# 객체지향
# 디자인패턴
# 싱글턴
# 상속
# 인터페이스
# 객체
객체지향 프로그래밍이 뭔가요?
붕어빵으로만 배워온 객체지향. 속 시원하게 알려드립니다.
# 객체지향
# 상속
# interface
# 객체
...
🌏 Why not change the world?