팩토리 패턴

객체를 사용하는 측에서 바로 생성하지 않고 중간에 생성을 전담하는 객체를 두고 생성하여 결합도를 낮춘다.

예제

동물 카페(AnimalCafe)에서 동물을 만날 수 있는데 현재는 토끼만 만날 수 있다.

interface Animal { /* ... */ }
class Rabbit implements Animal { /* ... */ }

class AnimalCafe {
  public Animal meet() {
      Animal animal = new Rabbit();
    // 기타 로직(등록, 지불 등)
    return animal;
  }
}

위 코드에서 meet() 클래스는 고객에게 제공할 동물을 선택하고, 제공하기 위해서 기타 로직을 수행한다. 이후 추가 케이스로 강아지(Puppy)가 추가되었고 카페에선 오전엔 토끼, 오후엔 강아지를 제공하려고 한다.

class Puppy implements Animal { /* ... */ }

class AnimalCafe {
  public Animal meet() {
    Animal animal;
      if (지금시간 == 오전)
      animal = new Rabbit();
    else
      animal = new Puppy();
    // 기타 로직(등록, 지불 등)
    return animall;
  }
}

또 다른 동물 종류가 추가되면 meet() 메소드의 수정이 지속적으로 필요하다. meet() 메소드는 조건에 따라서 제공할 동물을 선택하는 작업고객에게 동물을 제공하기 위한 작업 이 두 가지 작업에 대한 책임을 가지고 있다. 두 가지 작업 중 하나라도 변경이 필요하면 meet() 메소드는 수정되어야한다. meet() 메소드가 하나의 책임만을 가지도록 변경하기 위해 조건에 따라서 제공할 동물을 선택하는 작업을 아래처럼 분리할 수 있다.

class AnimalFactory {
  public static Animal pick() {
    if (지금시간 == 오전)
      return Rabbit();
    else
      return new Puppy();
  }
}

class AnimalCafe {
  public Animal meet() {
    Animal animal = AnimalFactory.pick();
    return animall;
  }
}

AnimalCafe 클래스에서 조건에 따라 동물을 생성하는 로직을 AnimalFactory 클래스의 pick() 메소드로 분리하였다. 이로써 AnimalCafe#meet() 메소드의 고객에게 동물을 제공하기 위한 작업에 대한 책임만을 가지게 되었다.

싱글톤 패턴 활용

상황에 따라서 매번 인스턴스를 생성할 필요가 없을 경우 싱글톤 패턴 적용을 아래와 같이 고려할 수 있다.

class Pupply implements Animal() {
  private Animal puppy;

  public static Animal getInstance() {
    if (puppy == null)
      puppy = new Puppy();

    return puppy;
  }
}

템플릿 메서드 패턴 활용

// TODO

싱글톤 패턴

클라이언트가 사용하려는 객체가 클라이언트의 라이프 사이클 동안 단 하나의 인스턴스임을 보장할 수 있는 패턴. 아래와 같은 상황에서 활용될 수 있다.

  • 공유 자원 접근
  • 복수의 시스템이 하나의 자원에 접근할 때
  • 유일한 객체가 필요할 때
  • 값의 캐시가 필요할 때

예제

모든 사람이 사용할 수 있는 공용 자원인 단 한대의 TV

class Tv {
  private static Tv tv;

  private int nowChannelNo = 0;

  private Tv tv() {}

  public static Tv getInstance() {
    if (tv == null)
      tv = new Tv();

    return tv;
  }

  public void changeChannel(int channerNo) {
    this.nowChannelNo = channerNo;
  }
}

멀티쓰레드 환경에서의 싱글톤

여러 쓰레드가 위 앞선 예제의 getInstance() 메소드에 접근할 경우 if (singletone == null) 접근 시점에 따라 여러 Singletone 객체를 생성할 수 있다. 이를 방지하기 위해 바로 초기화 하는 방법동기화 방식이 있다.

// 바로 초기화 하는 방식
class Tv {
  private static Tv tv = new Tv();;

  public static Tv getInstance() {
    return tv;
  }


}

// 동기화(synchronized 사용)
class Tv {
  private static Tv tv;

  public synchronized static Tv getInstance() {
    if (tv == null)
      tv = new Tv();

    return tv;
  }
}

싱글톤 vs Statis Method

정적 변수와 메소드들로만 이루어진 정적 클래스를 구성하면 싱글톤 패턴의 클래스와 같은 역할을 할 수 있다. 하지만 인터페이스를 구현해야하는 경우 정적 메소드를 사용할 수 없다. 위의 예제의 경우 Tv를 사용할 때 interface Tv를 구현한 TvA, TvB가 있다고 할 때, 클라이언트에서 Tv를 참조하고있고, 경우에 따라 TvA의 인스턴스, TvB의 인스턴스를 사용한다고 하면 이는 정적 클래스로 구현이 불가능하다.

전략 패턴

객체의 행위를 캡슐화하여 분리한다.

예제

로봇은 움직일 수 있고 공격할 수 있다. 각 로봇의 타입에 따라서 움직이거나 공격하는 방식이 다르다.

interface Robot {
  void move();
  void attack();
}

class RobotA implements Robot {
  void move() { do("로봇 A가 걸어갑니다") }
  void attack() { do("로봇 A가 총을 사용합니다.") }
}

class RobotB implements Robot {
  void move() { do("로봇 B가 뛰어갑니다.") }
  void attack() { do("로봇 B가 화염방사기를 사용합니다.") }
}

class Client {
  main() {
    RobotA a = new RobotA();
    RobotB b = new RobotB();
    a.move()
    a.attack()
    b.move()
    b.attack()
  }
}
  • 로봇 A가 날아가게 하려면? => 아래와 같은 기존 코드 변경이 필요하다.
  • class RobotA implements Robot { void move() { // do("로봇 A가 걸어갑니다") do("로봇 A가 날아갑니다") } ... }
  • 로봇C를 추가하고 B와 똑같은 방식으로 동작하게하려면? 화염방사기가 고장나서 물대포 방사기로 변경하여야된다면? => 두 곳의 수정이 필요하다.
  • class RobotB implements Robot { ... void attack() { do("로봇 C가 물대포 방사기를 사용합니다.") } } class RobotB implemenets Robot { ... void attack() { // do("로봇 B가 화염 방사기를 사용합니다.") do("로봇 B가 물대포 방사기를 사용합니다.") } } class RobotB implemenets Robot { void move() { do("로봇 C가 뛰어갑니다.") } void attack() { // do("로봇 B가 화염 방사기를 사용합니다.") do("로봇 C가 물대포 방사기를 사용합니다.") } }

수행하는 행위를 캡슐화하여 외부로 추출하여 사용할 수 있다.

interface MoveStrategy { void move(); }
class WalkMoveStrategy implements MoveStrategy { void move() { do("걷는다") }
class FlyMoveStrategy implements MoveStrategy { void move() { do("난다") }

interface AttackStrategy { void attack(); }
class GunAttackStrategy implements AttackStrategy { void attack() { do("총 공격") }
class FireAttackStrategy implements AttackStrategy { void attack() { do("화염방사기 공격") }

interface Robot {
  void move(MoveStrategy moveStrategy);
  void attack(AttackStrategy attackStrategy);
}

class RobotA implements Robot {

  public RobotA(MoveStrategy moveStrategy, AttackStragegy attackStrategy) {
    setMoveStrategy(moveStrategy)
    setAttackStrategy(attackStrategy)
  }

  void move() { moveStrategy.move() }
  void attack() { attackStrategy.attack() }
}

class RobotB implements Robot {

  public RobotA(MoveStrategy moveStrategy, AttackStragegy attackStrategy) {
    setMoveStrategy(moveStrategy)
    setAttackStrategy(attackStrategy)
  }

  void move() { moveStrategy.move() }
  void attack() { attackStrategy.attack() }
}

MoveStrategy, AttackStrategy만 변경하여 로봇에 주입함으로써 로봇의 움직임, 공격 방식을 수정하거나 기존에 만들었던 전략을 활용 가능함으로써 재활용도 가능해졌다.

상태 패턴

객체가 상태(객체가 가질 수 있는 어떤 조건이나 상황)가 변함에 따라 수행할 수 있는 행위를 외부로 분리하여 상태의 변경에 유연하게 대처할 수 있도록 한다.

예제

OFF, 약풍이 있는 선풍기 객체를 설계한다. 여기서 OFF, 약풍은 선풍기 객체의 상태에 해당한다.

class Fan {
  private static int OFF = 0;
  private static int 약풍 = 1;
  private int 현재상태 = OFF;

  ...
  public push(int signal) {
    if (signal == OFF)
      끈다(); 현재상태=OFF;
    else if (signal == 약풍)
      약풍(); 현재상태=약풍;
  }
}

여기서 강풍이 추가된다면 아래의 변경이 필요하다. => 새로운 상태가 추가될 때마다 그 상태를 처리하기 위한 기존 클래스의 수정이 지속적으로 발생하게 된다.

class Fan {
  ...
  private static int 강풍 = 2;
  ...

  public void push(int signal) {
    ...
    else if (signal == 강풍)
      강풍(); 현재상태=강풍;
  }
}

여기서 변경되는 부분은 상태, 고정되는 부분은 선풍기가 상태에 해당하는 행위를 실행시키는 책임이다. 아래와 같이 상태를 분리할 수 있다.

interfcae State {
  State action();
}

class Off implements State { State action() { 끈다() } }
class 약풍 implements State { State action() { 약풍() } }

class Fan {
  private int 현재상태;

  public void push(State state) {
    현재상태 = state.action();
  }
}

선풍기 객체는 상태 인터페이스만 참조하고 구체적인 구현에 의존하지 않는다. 강풍이 추가된다면 선풍기 객체 Fan에 변경이 필요한 것이 아니라 새로운 상태를 추가하기만 하면 된다.

class 강풍 implements State { State action() { 강풍() } }

싱글톤과의 결합

앞선 예제에서 상태를 생성할 때 매번 새로운 인스턴스를 생성할 필요가 없을 경우 싱글톤 패턴을 적용할 수 있다.

커맨드 패턴

예제

운동 로봇은 여러 모드가 있는데 그 중 축구를 할 수 있게 하는 축구 모드가 있고 리모컨으로 조정할 수 있다.

class SoccerRobot {
  void do() { 축구() }
}

class RemoteController {
  SoccerRobot soccerRobot;

  void push() { soccerRobot.do() }
}
  • 농구 로봇이 필요하면? => 농구 로봇을 추가하고 리모컨을 수정한다 => 기존 코드(RemoteController)의 변경이 필요하다.
  • class BaseballRobot { void do() { 농구() } void push() { soccerRobot.do() } } class RemoteController { BaseballRobot baseballRobot; ... }
  • 버튼을 눌렀을 때 이전 모드에서 변경된 모드로 동작하게 한다. 이전 모드가 축구라면 농구를, 농구라면 축구를 한다.새로운 로봇이 추가되고 새로운 조건이 추가된다면? => RemoteController의 수정이 계속 요구된다.
  • class RemoteController { final int BASEBALL = 1; final int SOCCER = 2; BaseballRobot baseballRobot; SoccerRobot soccerRobot; int nowState = BASEBALL void push() { if (nowStatue == BASEBALL) nowState = SOCCER; soccerRobot.do(); else if (nowStatus == SOCCER) nowState = BASEBALL; baseballRobot.do(); } }

여기서 계속 변하는 것은 버튼을 눌렀을 때 할 행위이다. 이 행위를 캡슐화하여 외부로 추출한다.

interface Command { void execut(); }
class SoccerCommand implements Command {
  SoccerRobot soccerRobot;
  void execute() { soccerRobot.do() }
}
class BaseballCommand implements Command {
  BaseballRobot baseballRobot;
  void execute() { baseballRobot.do() }
}

class RemoteController {
  Command command;

  void push() {
    command.execute();
  }
}

리모컨의 버튼을 눌렀을 때 일어날 행위를 추가하거나 변경하고 싶다면 Command 인터페이스를 구현한 구현체만 추가하면 되고 기존 클래스의 변경이 필요하지 않다.

옵저버 패턴

예제

경마 게임에서 말이 도착하면 바로 전광판에 보여주는 클래스를 설계한다.

class ArrivalRecorder {
  List<Integer> rank;
  ScoreBoardPrinter scoreBoardPrinter;

  void crossed(Int horseNumber) {
    rank.add(horseNumber);
    ScoreBoardPrinter.update();
  }
}

class ScoreBoardPrinter {
  ArrivalRecorder recorder;

  public void update() { 화면출력(recorder.getRank()); }
}

class Client {
  main() {
    ArrivalRecorder recorder = new ArrivalRecorder();
    ScoreBoardPrinter scoreBoardPrinter = new ScoreBoardPrinter(recorder);
    recoder.scoreBoardPrinter = scoreBoardPrinter;
  }
}
  • 다른 종류의 스코어 보드를 사용한다면? 예를들어 소리로 말하는 ScoreBoardSpeaker로 변경한다면? => 기존 클래스의 변경이 필요하다.
  • class ArrivalRecorder { ... ScoreBoardSpeaker scoreBoardSpeaker; ... } class ScoreBoardSpeaker { ... }
  • 여러 종류의 스코어 보드를 사용한다면? Printer, Speaker 방식 둘 다 사용하고 싶다, 2개의 Speaker를 사용하고 싶다 => 기존 클래스의 변경이 필요하다.
  • class ArrivalRecorder { ... List<ScoreBoardSpeaker> scoreBoardSpeaker; ScoreBoardPrinter scoreBoardPrinter; ... }

변하는 것은 ArrivalRecorder가 알려줘야 할 대상, 즉 Recorder에 새로운 이벤트가 생기면 그 이벤트를 받아서 처리해야되는 대상과 그 대상을 ArrivalRecorder에 등록하는 부분.

interface Observer { abstract void update(); }

abstract class Subject {
  private List<Observer> observers = new ArrayList<Observer>();

  void attach(Observer observer) { ... }
  void detach(Observer observer) { ... }
  void notify() { 
    for (Observer observer: observers) {
      observer.update()
    }
  }
}

class ArrivalRecorder extends Subject {
  List<Integer> rank;
  void crossed(Int horseNumber) {
    rank.add(horseNumber);
    this.notify();
  }
}

class ScoreBoardPrinter implements Observer {
  void update() { 화면출력(recorder.getRank()); }
}
class ScoreBoardSpeaker implements Observer {
  void update() { 스피커출력(recorder.getRank()); }
}

새로운 출력 방식이 필요하면 Observer를 구현한 새로운 SocreBoard를 구현하고 ArrivalRecorder에 등록하면 기존 코드 수정 없이 새로운 출력 방식의 추가나 변경이 가능하도록 된다.

데코레이터 패턴

여러 기능을 구현한 클래스의 조합을 구현하고자 할 때 사용할 수 있다.

예제

스포츠를 할 수 있는 로봇이 있다. 로봇은 현재 축구 기능을 제공한다.

interface Robot {
  void do();
}

class SoccerRobot implements Robot {
  void do() {
    축구();
  }
}
  • 다른 기능을 하는 로봇이 추가된다면? 예를들어 농구를 하는 로봇, 달리기를 하는 로봇
  • 여러 기능을 조합한 로봇을 원한다면? 예를들어 농구, 축구를 하는 로봇.

각 기능별로 Robot 인터페이스를 구현한 클래스를 만들어야되고, 조합의 경우 모든 구현체에 대한 조합을 하나하나 구현해야 한다. 축구, 농구, 달리기 로봇을 조합하면 축구+농구, 축구+달리기, 농구+달라기 로봇을 구현해야한다.

각 추가 기능별로 개별 클래스를 설계하고 객체의 조합을 담당하는 클래스(데코레이터)를 만든다.

interface Robot {
  void do();
}

class Defaultrobot implements robot {
  void do() {
    기본_동작();
  }
}


abstract class RobotDecorator extends Robot {
  Robot robot;

  void do() {
    robot.do();
  }
}

class SoccerDecorator extends RobotDecorator {

  SoccerDecorator(Robot robot) {
    super(robot)
  }

  void do() {
    robot.do();
    축구();
  }
}

class BaseballDecorator extends RobotDecorator {

  BaseballDecorator(Robot robot) {
    super(robot)
  }

  void do() {
    robot.do();
    농구();
  }
}

다른 기능의 로봇이 필요할 경우 RobotDecorator를 구현한 신규 기능의 클래스를 구현하고 아래와 같이 클라이언트에서 사용할 수 있다.

class RunningDecorator extends Decorator {
  ...
}

main() {
  Robot runningRobot = new RunningDecorator(new DefaultRobot());
  runningrobot.do();
}

여러 기능을 조합한 로봇의 경우 아래와 같이 구현할 수 있다.

main() {
  Robot runningAndSoccerRobot = new RunningDecorator(
    new SoccerDecorator(
      new DefaultRobot()));
  runningAndSoccerRobot.draw();
}

템플릿 메소드 패턴

전체적으로 동일하면서 부분적으로 다른 구문으로 구성된 메소드의 코드 중복을 줄인다. 동일한 기능을 상위 클래스에서 정의하고 변경이 발생하는 부분만 하위 클래스에서 구현한다.

예제

차가 출발하려면 문이 닫혀있는지 확인해야 한다.

class Car {
  move() {
    문_확인()
    출발();
  }
}

여기서 여러 종류의 차가 있다면 아래와 같이 설계해볼 수 있다.

interface Car {
  move();
}

class CarA implements Car {
  move() {
    문_확인();
    CarA방식_출발();
  }
}

class CarB implements Car {
  move() {
    문_확인();
    CarB방식_출발();
  }
}

문_확인() 부분이 계속 중복되는 문제가 있다. 예를들어 창문_확인() 동작이 추가되어야 한다면? -> Car 구현체를 모두 찾아 변경해야 한다.
여기서 변하는 부분은 출발 전 확인하는 부분. 이 행위를 별도의 클래스로 분리한다.

abstrafct class CarTemplate {
  move() {
    문_확인();
    창문_확인();
    moveCar();
  }

  protected abstract moveCar();
}

class CarA extends CarTemplate {
  protected moveCar() {
    CarA방식_출발();
  }
}

다른 방식이 추가된다면 CarTemplate 클래스의 move() 메소드만 수정하면 된다.

컴퍼짓 패턴

객체가 서로 '부분-전체'의 조합을 가지고 전체에서 부분의 공통적인 동작들을 요구할 때 활용할 수 있다.

예제

A, B 부품으로 구성된 로봇. 각 부품 또는 전체(부품의 합)의 전력 소모 합을 제공한다.

class Robot {
  A a;
  B b;

  int getPower() {
    return a.getPower() + b.getPower() + c.getPower();
  }
}

class A {
  int getPower() {
    return 100;
  }
}

class B {
  int getPower() {
    return 50;
  }
}
  • 부품 C가 추가된다면? -> 새로운 클래스 C를 만들고 Robot의 멤버를 수정한다. 즉 기존 클래스의 수정이 필요하다.

    class C {
      int getPower() {
        return 400;
      }
    }
    
    class Robot {
      ...
      C c;
    
      int getPower() {
        return a.getPower() + b.getPower() + c.getPower();
      }
    }

추가되는 클래스를 추상화하고, 추상화된 클래스를 변경없이 다룰 수 있는 구조로 설계한다.

interface RobotPart {
  int getPower();
}

class A implements RobotPart { ... }
class B implements RobotPart { ... }
class C implements RobotPart { ... }

class Robot {
  List<RobotPart> robotParts = new ArrayList<>();

  void add(RobotPart part) {
    robotParts.add(part);
  }

  int getPower() {
    return robotParts.getPower().sum()
  }
}

Reference

'Program language > Java' 카테고리의 다른 글

run java project with h2 without installation  (0) 2022.01.22
Java Reflection  (0) 2018.04.03
implementation 'com.h2database:h2:1.4.200'
public class App {
  public static void main(String[] args) throws Exception {

    final String[] H2_ARGS = new String[]{
      "-web"
      , "-webPort", "9091"
      , "-webAdminPassword", ""
      , "-browser"
      , "-tcp"
      , "-tcpAllowOthers"
      , "-tcpPort", "9090"
      , "-key", "test", "test"
    };


    Server h2TcpServer = Server.createTcpServer(H2_ARGS);
    Server h2WebServer = Server.createWebServer(H2_ARGS);
    h2TcpServer.start();
    h2WebServer.start();
    System.out.println(h2TcpServer.getStatus());
    System.out.println(h2WebServer.getStatus());
  }
}

Create EntityManager

implementation 'org.hibernate:hibernate-entitymanager:5.6.4.Final'
Class.forName("org.h2.Driver");
Connection con = DriverManager.getConnection(
"jdbc:h2:mem:tcp://localhost:9090/test", "sa", "");

EntityManagerFactory emf =
Persistence.createEntityManagerFactory("test_persistence_unit");
EntityManager entityManager = emf.createEntityManager();
// /main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
  <persistence-unit name="test_persistence_unit">
    <properties>
      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
      <property name="javax.persistence.jdbc.user" value="sa"/>
      <property name="javax.persistence.jdbc.password" value=""/>
      <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:tcp://localhost:9090/test"/>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
      <property name="hibernate.use_sql_comments" value="true"/>
    </properties>
  </persistence-unit>
</persistence>

'Program language > Java' 카테고리의 다른 글

디자인 패턴 cheat sheet  (0) 2022.04.10
Java Reflection  (0) 2018.04.03

Calculate

print(15/7)                # 나눗셈, 2
print(15//7)            # 몫, 2
print(15%7)                # 나머지, 1
print(2**3)                # 제곱근, 8

Variable Type

Type

  • String
  • Boolean
  • Integer
  • Float
v = 1
print(type(v))        # int

Tuple, Dictionary, Set

Tuple

수정이 안되는 엘리먼트 집합 자료형

# define
tuple = ()
tupleA = tuple()
tupleA = (1, 2)
tupleB = (3, 4)

print( tupleA(0) )        # 1, 인덱스 접근 가능

tupleA + tupleB            # (1, 2, 3, 4)
tupleA * 2                # (1, 2, 1, 2)


# use case
## switch x, y without temporary variable
x = 1, y = 2
x, y = y, x

## return two varible
function1(x, y)
  return x+y, x*y

## List to Tuple, Tuple to List
tupleA = tuple( [1,2,3] )

listA = list( (1,2,3) )

Dictionary

수정이 가능한 key, value 형태의 데이터 셋

dic = {}
dic = dic()
dic = {'key1': 'value1', 'key2', 'value2'}

# get
dic['key1']        # value1

# add or edit
dic['key3'] = 'value3'

# delete
del dic['key3']

# functions
dic.items()        # get tuple (key, value)
dic.keys()        # get key array
dic.values()    # get value array

Set

집합연산을 지원하는 순서와 중복이 없는 데이터 셋. 순서가 없기때문에 인덱스로 접근 불가

setA = {1, 2, 3}
setA = set()
setA = set(1)
setA = set(1, 2, 3)        # 한 개 이상일 경우 { } 사용해야 함

# use
## check contains
'apple' in setA            # false
1 in setA                # true


## operation
setA = {1, 2, 3}
setB = {3, 4}

setA & setB            # {3}, 교집합
setA | setB            # {1, 2, 3, 4}, 합집합
setA - setB            # {1, 2}, 차집합
setA ^ setB            # {1, 2, 4}, xor

## list의 중복 제거
list(set(listA))

input, print

name = input("type your input : ")                # kim
print(name)        # kim

print("Hello, ", name)                            # Hello, kim
print("Hello, {0}, {1}".format(name, "woo"))    # Hello, kim, woo
print("Hello, {1}, {0}".format(name, "woo"))    # Hello, woo, kim
print("Hello, {0}, {0}".format(name, "woo"))    # Hello, kim, kim


v = 0.126
print( format(v, ".2f") )        # 0.13
print( format(v, ".4f") )        # 0.1260
# %s : string, %c : character, %d : int, %f : float

String

str = """
Hello,
World!
"""
str = "Hello, World!"


len(str)                    # 13
str.count("l")                # 3
str.find("e")                # 1
str.replace("Hello", "Hi")    # Hi, World!
str[0]            # H
str[0:2]        # He
str[:2]            # He
str[0:-2]        # Hello, Wor
str.split(',')    # ["Hello", " World!"]


str = "   strip   "
str.strip()        # strip

Array

arr = [1, "a", 3.5]
print( arr[2] )                # 3.5
print( arr[-1] )            # 3.5
print( arr[1:3] )            # ["a", 3.5]
print( arr.append(3) )        # [1, "a", 3.5, 3]
print( arr.remove("a") )    # [1, 3.5, 3]

del arr[1]
print( arr )                # [1, 3]
arr.insert(1, 5)
print( arr )                # [1, 5, 3]

arr.sort()        
print( arr )    # [1, 3, 5]
arr.reverse()    
print( arr )    # [5, 3, 1]

For, While, If

#for
for element in ['a', 'b', 'c', 'd']:
  print(element)        # a, b, c, d

for element in range(3)        # or range(0:3)
  print(element)        # 0, 1, 2

# while
while true:
  print("Hello World")
  break;

# if elif else
if n == 1:
  print('n is 1')
elif n == 2:
  print('n is 2')
else:
  print('n is ', n)

Class

# define
class People():
  id = 0
  name = ""

  def hello(self, args2):
      print("arg1 : ", arg1)
      return "Hello, " + self.name

Reflection 이란?

클래스의 이름만으로도 객체의 정보를 가져올 수 있는 기능.

왜쓰나?

동적으로 클래스를 호출해야 될 때 사용. 코드를 작성하는 시점이 아니라 JVM이 application이 running하고 있을 때 어떤 동작(사용자의 요청 또는 로직)에 의해서 사용해야 하는 class가 정해질 때 사용된다. JSON, XML 파서나 Spring에서 Bean을 생성할 때도 쓴다고 한다.

기본 사용법


의 모양으로 class를 얻는다. 여기서 className은 패키지를 포함한 클래스명을 적어야한다. 이렇게 Class 객체를 얻으면 해당 클래스에 대한 annotation, field, method, constructor 등 거의 모든 정보에 접근 가능하고 해당 정보를 사용해서 인스턴스를 생성까지 할 수 있다.


사용예시

내가 받는 예상 가능 한 일정한 모양의 데이터(DB 쿼리 결과 등)를 조회해서 똑같은 모양의 클래스와 매핑하고자 할 때 사용을 하였었다. 파서도 유사한 로직을 수행할 것으로 예상된다. 만약에 어떤 api를 조회했을 때


1
2
3
4
5
{
    "name" : "rura6502"
    "age" : 99
    "country" : "korea"
}

cs


위와 같은 데이터가 나온다고 하면

1
2
3
4
5
class Person {
  String name;
  int age;
  String country;
}
cs


위 모양의 클래스를 만들어서 매핑 시키면 활용가능한 인스턴스를 얻을 수 있을 것이다. 대충 Person 클래스의 매핑 로직을 짜면


1
2
3
4
5
6
public Person mappingForPerson(Map<String, Object> data) {
    Person person = new Person();
    person.name = data.get("name");
    ....
    return person;
}
cs


위와 같은 메소드가 만들어 질 것 같은데 만약 Person 말고 다른 종류의 다양한 객체가 있으면 해당 객체의 갯수 만큼 메소드를 만들어야 된다. 하지만 여기서 reflection을 활용하면 데이터의 클래스 오브젝트를 받아서 클래스의 필드, 생성자 등 클래스에 대한 정보를 알아낸 다음에 순서에 따라서 착착 매핑시켜주고 생성자를 호출해서 만든 인스턴스를 반환하면 여러가지 클래스를 감당할 수 있는 하나의 메소드를 만들수 있게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public <T> T mapToClassMapping(Map<String, Object> data, Class<T> mappingClass) {
    
  Set<String> keys = data.keySet();
  Field[] fields = mappingClass.getDeclaredFields();
  Class paramTypes[] = new Class[fields.length];
  Object args[] = new Object[fields.length];
  int i=0;
  for (String key : keys) {
    paramTypes[i] = fields[i].getType();
    args[i] = paramTypes[i].cast(data.get(key));
    i++;
  }
  Constructor<T> ct = mappingClass.getConstructor(paramTypes);
  return ct.newInstance(args);
}
cs


간단하게 설명하자면 매핑하고자 하는 클래스 오브젝트를 받고 생성에 필요한 필드, 필드 타입을 분석해서 Map에서 나오는 데이터와 차례차례 매핑시켜서 인스턴스를 리턴하는 메소드이다. 이 코드는 테스트용 코드로 실제로 사용하려면 약간의 고급화 과정이 필요하다. 맵의 key와 맞는 필드에 세팅한다던가 등등.. 단순히 reflection의 기능을 이해하고자 만든 코드이다. 아래는 테스트 코드.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  public static void main(String[] args) throws Exception {
    class Person {
      String name;
      Integer age;
 
      public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
      }
    }
    Map<String, Object> testMap = new HashMap<String, Object>();
    testMap.put("name""rura6502");
    testMap.put("age"20);
 
    Person rura6502 = PrestoUtil.mapToClassMapping(testMap, Person.class);
    System.out.println(rura6502.name);
    System.out.println(rura6502.age);
  }
cs


참조

reflection을 조심해서 써야되는 이유 - https://stackoverflow.com/a/43014858/6563164


'Program language > Java' 카테고리의 다른 글

디자인 패턴 cheat sheet  (0) 2022.04.10
run java project with h2 without installation  (0) 2022.01.22

+ Recent posts