[직관적인 클린 코드를 위해] IF - ELSE IF 문 줄이기 2



이번 블로그 글은 저번에 이은 '[직관적인 클린 코드를 위해] IF - ELSE IF 문 줄이기 2' 입니다.


저번에는 동시에 확인해야 하는 조건이 하나일 경우에 IF, ELSE IF 문을 줄이는 방법을 봤습니다.


하지만 만약 동시에 확인해야 할 조건이 2개면 어떨까요?



public static void main(String[] args) {
Map<String, Alphabet> typeMap = new HashMap<>();
typeMap.put("A", new A());
typeMap.put("B", new B());
typeMap.put("C", new C());

String type = "B";

if(첫번째 조건) {
typeMap.get(type).test();
}
else if(첫번째 조건) {
typeMap.get(type).test();
}
else if(첫번째 조건) {
typeMap.get(type).test();
}
}



어쩔 수 없이 IF, ELSE IF 문을 사용해야 하는 것일까요?


예를 들어 상황을 가정하겠습니다.

DB의 여러 테이블에서 데이터를 받는데 이를 테이블 명과 Insert, Update, Delete 로 동시에 구분해서 수행해야 한다고 가정하겠습니다.


공통적인 부분으로 묶어보겠습니다.


  • 각각의 테이블은 Insert, Update, Delete가 일어납니다.

interface Insert {
public void insert();
}

class A implements Insert{
@Override
public void insert() {

}
}

class B implements Insert{
@Override
public void insert() {

}
}

class C implements Insert{
@Override
public void insert() {

}
}



Insert 패키지를 만들고 insert 추상매서드를 가진 인터페이스를 생성했습니다.

그리고 이 인터페이스를 implements 하는 3개의 클래스를 각각 만들었습니다.

Update와 Delete도 똑같이 했습니다.


하지만 이렇게 해도 1번 글과 같이 Map에 넣어 한번에 알아서 분류되고 원하는 value가 나오게 하려면 Insert, Update, Delete 인터페이스 타입을 value로 가진 각각의 Map이 필요합니다.

다시 공통적인 요소를 생각해보면 Insert, Update, Delete는 DB의 Crud 중 하나입니다.



interface Crud {
public void execute();
}

interface Insert extends Crud{
public void insert();
}

class A implements Insert{
@Override
public void execute() {
insert();
}

@Override
public void insert() {

}
}



Crud 인터페이스를 만들어 이를 Insert, Update 인터페이스에 상속 시키고 A 클래스에서는 이를 구현합니다.

그리고 두가지 조건을 처리하기 위해 Enum 클래스를 만들어 사용했습니다.


enum CommandEnum {
INSERT_A("A", "insert"),
UPDATE_A("A", "update"),
INSERT_B("B", "insert"),
UPDATE_B("B", "update"),
INSERT_C("C", "insert"),
UPDATE_C("C", "update");

private String tableName;
private String queryType;

CommandEnum(String tableName, String queryType) {
this.tableName = tableName;
this.queryType = queryType;
}

public String getTableName() {
return this.tableName;
}

public String getQueryType() {
return this.queryType;
}
}



그리고 이런 Enum을 사용하기 위해 따로 Mapper 클래스를 만들었습니다.

Mapper 클래스에서는 EnumMap을 생성하고 이를 for - each 문으로 값을 두가지 조건에 합당하는 Crud 객체를 리턴합니다.



class QueryTypeMapper {
private EnumMap<CommandEnum, Crud> commandEnumMap = new EnumMap<CommandEnum, Crud>(CommandEnum.class);

QueryTypeMapper() {
commandEnumMap.put(CommandEnum.INSERT_A, new InsertA());
commandEnumMap.put(CommandEnum.UPDATE_A, new UpdateA());
commandEnumMap.put(CommandEnum.INSERT_B, new InsertB());
commandEnumMap.put(CommandEnum.UPDATE_B, new UpdateB());
commandEnumMap.put(CommandEnum.INSERT_C, new InsertC());
commandEnumMap.put(CommandEnum.UPDATE_C, new UpdateC());
}

public Crud getCommand(String tableName, String type) {
for(CommandEnum commandEnum : commandEnumMap.keySet()) {
if(commandEnum.getTableName().equals(tableName) && commandEnum.getQueryType().equals(type)) {
return commandEnumMap.get(commandEnum);
}
}
throw new IllegalStateException("Classify command not found. ");
}
}



이렇게 만들어진 QueryTypeMapper 클래스와 CommandEnum enum클래스를 이용해 IF - ELSE IF 문으로 어지러워 질뻔한 코드가 단 두줄로 변하게 됩니다.


아래에 전체 코드가 있습니다. InsertA 클래스만 있지만 나머지 클래스들도 같은 방식이라 넣지 않았습니다.


간략하게 짜고 바쁘게 하다보니 클래스와 매서드, 변수의 네이밍에 신경 못쓴점은 양해 부탁드립니다.



public class CleanCode {

public static void main(String[] args) {
String tableName = "B";
String type = "insert";

QueryTypeMapper queryTypeMapper = new QueryTypeMapper();
queryTypeMapper.getCommand(tableName, type).execute();
}
}



class QueryTypeMapper {
private EnumMap<CommandEnum, Crud> commandEnumMap = new EnumMap<CommandEnum, Crud>(CommandEnum.class);

QueryTypeMapper() {
commandEnumMap.put(CommandEnum.INSERT_A, new InsertA());
commandEnumMap.put(CommandEnum.UPDATE_A, new UpdateA());
commandEnumMap.put(CommandEnum.INSERT_B, new InsertB());
commandEnumMap.put(CommandEnum.UPDATE_B, new UpdateB());
commandEnumMap.put(CommandEnum.INSERT_C, new InsertC());
commandEnumMap.put(CommandEnum.UPDATE_C, new UpdateC());
}

public Crud getCommand(String tableName, String type) {
for(CommandEnum commandEnum : commandEnumMap.keySet()) {
if(commandEnum.getTableName().equals(tableName) && commandEnum.getQueryType().equals(type)) {
return commandEnumMap.get(commandEnum);
}
}
throw new IllegalStateException("Classify command not found. ");
}
}



enum CommandEnum {
INSERT_A("A", "insert"),
UPDATE_A("A", "update"),
INSERT_B("B", "insert"),
UPDATE_B("B", "update"),
INSERT_C("C", "insert"),
UPDATE_C("C", "update");

private String tableName;
private String queryType;

CommandEnum(String tableName, String queryType) {
this.tableName = tableName;
this.queryType = queryType;
}

public String getTableName() {
return this.tableName;
}

public String getQueryType() {
return this.queryType;
}
}



interface Crud {
public void execute();
}



interface Insert extends Crud{
public void insert();
}



class InsertA implements Insert{
@Override
public void execute() {
insert();
}

@Override
public void insert() {
System.out.println("Class A insert method!");
}
}



제가 준비한 내용은 여기까지 입니다.

부족함이 많은 글임에도 끝까지 읽어주셔서 감사합니다.

다음에도 이와 같이 기본적인 내용이지만 괜찮다고 생각드는 것이 있으면 잘 정리해서 공유드리겠습니다.

감사합니다.


+ Recent posts