Singleton 패턴은 객체의 인스턴스를 한개만 생성하게 하는 패턴이며, 최최 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.
주로 공통된 객체를 여려개 생성해서 사용하는 상황에서 많이 사용된다.
Singleton 패턴은 multi-thread 환경에서는 동시성 이슈가 발생할 수 있다. 물론 해결방법도 있으니 쓸 수 없는것은 아니다.
public class Settings{
private Settings(){};
private static Settings settings = null;
public static Settings getSettings(){
if(settings == null){
settings = new Settings();
}
return settings;
}
}
#################################
public class FirstPage{
private Settings settings = Settings.getSettings(); //method 호출
}
코드를 살펴보면 private static 으로 Singleton 객체의 인스턴스를 선언후 다른 클래스에서 getSettings() 메서드가 처음 실행될때 settings 가 생성되고 아니라면 이미 생성 되었던 settings 인스턴스가 리턴된다.
기본 생성자 앞에 private 접근 제어자를 써주어 외부에서 새로운 객체의 생성을 막는 역할도 한다. 마찬가지로 static 변수로 선언된 settings 도 정적공간에 담기 위한 역할을 한다.
같은 기능이지만 서로 다른 전략(method) 을 가진 클래스들을 캡슐화하여 상호 교환할 수 있도록 하는 패턴이다.
즉, 옵셜들마다의 행동들을 모듈화해서 독립적으로 상호 교체 가능하게 만드는 것이다.
public class MyProgram{
private SearchButton searchButton = new SearchButton(this);
public void setModeAll(){
searchButton.setSearchStrategy(new SearchStrategyAll());
}
...
}
#######################################
interface SearchStrategy{
public void serach();
}
class searchStrategyAll implements SearchStrategy{
public void search(){...}
}
class searchStrategyImage implements SearchStrategy{
public void search(){...}
}
#######################################
public class SerachButton{
....
private SearchStrategy searchStrategy = new SearchStrategyAll();
public void setSearchStrategy(SearchStrategy _searchStrategy){
searchStrategy = _searchStrategy;
}
public void onClick(){
searchStrategy.serach();
}
}
코드를 살펴보면 SearchStrategy 인터페이스를 만들고 onClick 메서드에서 searchStrategy 의 serach() 함수만 상속받아서 사용한다.
setter 를 이용하여 searchStrategy를 SearchStrategy 인터페이스를 상속받은 다른 검색 전략으로 바꿀 수 있는 것이다.
객체가 상태에 따라 행위를 달리하도록 하는 패턴, 상태가 행위를 하게 한다는 의미로 동일한 동작을 객체의 상태에 따라 다르게 처리해할 때 사용된다.
State 패턴은 Strategy 패턴과 유사하며, 특정 상태마다 다르게 할일, 상태마다 할 일을 하나씩 모듈화해서 지정할 때 쓰인다.
즉, Strategy 패턴이, 지정된 특정 메소드가 모듈화된 모드에 따라 다르게 실행되도록 하는 것이라면, State 패턴은, 그 메서드가 실행될 때 모드가 전환되도록 하는 것이다.
public class ModeSwitch{
...
public void on Switch() {
modeState.toggle(this);
}
}
###################################
public interface ModeState{
public void toggle (ModeSwitch modeSwitch);
}
class ModeStateLight implements ModeState{
public void toggle (ModeSwitch modeSwitch){
...
modeSwitch.setState(new ModeStateDart());
}
}
class ModeStateDark implements ModeState{
public void toggle (ModeSwitch modeSwitch){
...
modeSwitch.setState(new ModeStateLight());
}
}
코드를 살펴보면 onSwitch() 함수가 실행되면 ModeStateLight 클래스의 toggle 함수가 실행되고 setState 함수에서 ModeStateDark 모드로 전환되는 것을 확인할 수 있다.
실행될 기능을 캡슐화 함으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴이다.
이벤트가 발생했을 때 실행될 기능이 다양하면서도 변경이 필요한 경우 이벤트를 발생 시키는 클래스 변경없이 재사용에 유용하다.
실행을 요구하는 호출자 클래스와 실제 기능을 실행하는 수신자 클래스 사이의 의존성을 제거한다.
즉 실행될 기능의 변경에서 호출자 클래스 수정없이 사용 가능하다.
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();
}
}
}
#################################################
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();
}
}
##############################################
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("앞의 물건 집어들기");
}
}
코드를 살펴보면 Robot, RobotKit 클래스와 Command 추상클래스가 선언되어 있다.
Command 추상 클래스에서 execute 추상 메서드로 선언되어 있어 자식 클래스인 TurnCommand, PickupCommand 에서 기능이 구현된다.
여기서 추상클래스는 객체 생성이 불가하다. 때문에 Command 추상 클래스에서 Robot 객체를 setRobot() 메서드를 통해 값을 지정한다.
TurnCommand, PickupCommand 클래스에서 기능을 구현하여 RobotKit 클래스에서 기본 변수값으로 Robot의 Command를 상속한 클래스의 객체를 ArrayList 로 담는다.
한 클래스의 인터페이스를 클라이언트에서 사용하고자하는 다른 인터페이스로 변환하며, 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스를 연결 가능하다.
인터페이스가 바뀌더라도 클라이언트와 구현된 인터페이스가 분리되어 있으므로 변경 내역은 어댑터에 캡슐화 되기에 클라이언트는 바뀔 필요가 없다.
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);
}
}
#########################################
class CommandOrderAdapter extends Command {
private Order order;
public CommandOrderAdapter (Order _order) {
order = _order;
}
public void execute () {
order.run(robot);
}
}
#########################################
public class MyProgram{
RobotKit robotkit = new Robotkit();
....
robotKit.addCommand(new CommandOrderAdapter(new MoveBackOrder(1));
}
코드를 살펴보면 위의 설명된 Command 패턴에 Adapter 를 적용하였다.
CommandOrderAdapter 클래스에서 멤버변수 Order 객체를 생성자로 받아온다. execute() 메서드가 실행되면 Order 객체의 run() 에 상속받은 내부 변수인 robot을 넣어서 메서드를 실행한다. Order 인터페이스를 상속받은 MoveBackOrder 클래스의 run() 메서드가 실행 되는 것이다.
마지막 어답터에 끼워 ArrayList 명령 리스트에 넣어주면 작동하게 된다.
https://www.youtube.com/watch?v=lJES5TQTTWE
https://www.youtube.com/watch?v=q3_WXP9pPUQ