TIL 240619

인자약·2024년 6월 19일

TIL

목록 보기
12/14

DesignPattern

https://refactoring.guru/design-patterns/java

1. Singleton

package singleton;

import java.time.LocalDateTime;

// app 전체에서 log 를 담당하는 클래스
// 이 클래스의 객체는 단 한개만 만들어서 사용되도록 한다.
public class Logger {
	
//	static {
//		// 미리 선 작업...
//		logger = new Logger();
//	}
	
	// private static Logger 변수
//	private static Logger Logger = new Logger();
	private static Logger logger;
	
	// 생성자를 private 로
	private Logger() {}
	
	// static 메소드로 외부에서 객체를 얻을 수 있는 방법 제공
	public static Logger getInstance() {
		if (logger == null) {
			logger = new Logger();
		}
		return logger;
	}
	
	public void log(String message) {
		LocalDateTime ldt = LocalDateTime.now();
		String date = ldt.getYear() + "/" + ldt.getMonthValue() + "/" + ldt.getDayOfMonth();
		String time = ldt.getHour() + ":" + ldt.getMinute() + ":" + ldt.getSecond();
		
		System.out.println("[" + date + " " + time + "] " + message);
	}
}

// 객체는 만들어진다. static으로 정적으로 할 뿐
package singleton;

public class Test {

	public static void main(String[] args) {
//		Logger logger = new Logger();
//		Logger logger2 = new Logger();
		// 객체를 만들던가 x
		// static ???
		
		Logger logger = Logger.getInstance();
		System.out.println(logger);
		logger.log("첫 번째 로그입니다.");
		
		m();
	}
	
	public static void m() {
		Logger logger = Logger.getInstance();
		System.out.println(logger);
		logger.log("두 번째 로그입니다.");
	}
}

2. MethodChain

package methodchain;

public class Calculator {
	private int first;
	private int second;
	
	public Calculator setFirst(int first) {
		this.first = first;
		return this;
	}
	public Calculator setSecond(int second) {
		this.second = second;
		return this;
	}
	
	public Calculator showAdd() {
		System.out.println("Add " + this.first + " + " + this.second + " = " + (this.first + this.second));
		return this;
	}
	
	public Calculator showSub() {
		System.out.println("Sub " + this.first + " - " + this.second + " = " + (this.first - this.second));
		return this;
	}
}
package methodchain;

public class Test {

	public static void main(String[] args) {
		Calculator calc = new Calculator();
//		// 3 + 5
//		calc.setFirst(3);
//		calc.setSecond(5);
//		calc.showAdd();
//		// 3 - 1
//		calc.setFirst(3);
//		calc.setSecond(1);
//		calc.showSub();
//		// 6 - 1
//		calc.setFirst(6);
//		calc.showSub();
		
		// method chain
		calc.setFirst(3).setSecond(5).showAdd().setSecond(1).showSub();
		
		// StringBuilder
		StringBuilder sb = new StringBuilder();
		sb.append("abc");
		sb.append("def");
		sb.append("123")
		  .append("456")
		  .append("789");
		

	}

}

3. Iterator

package iterator;

public interface Container {
	Iterator getIterator();
}
package iterator;

public interface Iterator {
	boolean hasNext();
	Object next();
}
package iterator;

public class StringContainer implements Container {

	String[] strArray = {"Hello", "Iterator", "Pattern"};
	
	@Override
	public Iterator getIterator() {
		// Iterator interface 를 구현한 객체를 return
		return new StringIterator();
	}

	private class StringIterator implements Iterator {

		int index; // default 0
		
		@Override
		public boolean hasNext() {
			if (index < strArray.length) return true;
			return false;
		}

		@Override
		public Object next() {
			// 사용자가 hasNext() 후 호출
//			return strArray[index++];
			
			// 좀 더 안전한 방법
			if (this.hasNext()) return strArray[index++];
			return null;
		}
		
	}
}
package iterator;

public class Test {

	public static void main(String[] args) {
		StringContainer container = new StringContainer();
		
		Iterator iter = container.getIterator();
		while (iter.hasNext()) {
			System.out.println(iter.next());
		}
		
	}

}

4. Factory

package factory;

public interface Transportation {
	void move();
}
package factory;

public class Car implements Transportation {

	@Override
	public void move() {
		System.out.println("자동차가 달립니다.");
		
	}

}
package factory;

public class SportsCar implements Transportation {

	@Override
	public void move() {
		System.out.println("스포츠카가 달립니다.");
		
	}

}
package factory;

public class Airplane implements Transportation {

	@Override
	public void move() {
		System.out.println("비행기가 날아갑니다.");
		
	}

}
package factory;

public class Hellicopter implements Transportation {

	@Override
	public void move() {
		System.out.println("헬리콥터가 날아갑니다.");
		
	}

}
package factory;

// XXXFactory 보통 Singleton 으로 구성
public class TransportationFactory {
	public static Transportation getTransportation(String clsf) {
		Transportation t = null;
		switch(clsf) {
			case "Air": t = new Airplane(); break;
			case "Car": t = new SportsCar(); break;
		}
		
		return t;
	}

}
package factory;

public class Test {

	public static void main(String[] args) {
		// 사용하는 쪽인 Test 가 Car 클래스를 인지하고 있어야 한다.
		// Car 대신 SportsCar 로 변경되는 경우 코드를 변경해야 한다.
//		Car car = new Car();
//		SportsCar car = new SportsCar();
		// 만들어진 객체가 Car(), SportsCar() 객체이든 Transportation 인터페이스에 있는
		// 메소드만 사용
//		Transportation car = new Car();
//		Transportation car = new SportsCar();
		
		// Car, Airplane 클래스를 몰라도 문자열로 필요한 객체를 얻을 수 있다.
		// 이 객체는 Transportation interface 를 구현한 것만 알고 있고 그것만 사용
		Transportation t = TransportationFactory.getTransportation("Air");
		t.move();
		
		Transportation t2 = TransportationFactory.getTransportation("Car");
		t2.move();
	}

}

5.Adapter

package adapter;

public interface LegacyFunc {
	int calc(int num);
}
package adapter;

public interface LegacyFuncAdapter {
	int calc(int num);
}
package adapter;

public class LegacyFuncImpl implements LegacyFunc {

	@Override
	public int calc(int num) {
		// Legacy 는 x 10
		return num * 10;
	}

}
package adapter;

public class LegacyFuncAdapterImpl implements LegacyFuncAdapter {

	// 기존 Legacy interface 및 구현체를 이용
	private LegacyFunc legacyFunc;
	
	public LegacyFuncAdapterImpl(LegacyFunc legacyFunc) {
		this.legacyFunc = legacyFunc;
	}
	
	// legacy 의 결과를 이어서 추가로 처리할 내용
	private int convertLegacyData(int legacyResult) {
		System.out.println(legacyResult);
		return legacyResult * 2;
	}
	
	@Override
	public int calc(int num) {
		// Legacy 는 x 10
		return convertLegacyData(legacyFunc.calc(num));
	}

}
package adapter;

public class Test {

	public static void main(String[] args) {
		// Legacy system 처리
//		LegacyFunc legacyFunc = new LegacyFuncImpl();
//		System.out.println(legacyFunc.calc(10));

		// New system 처리
		LegacyFunc legacyFunc = new LegacyFuncImpl();
		LegacyFuncAdapter legacyFuncAdapter = new LegacyFuncAdapterImpl(legacyFunc);
		System.out.println(legacyFuncAdapter.calc(10));
	}

}

6. Builder

package builder;

public class NormalBook {
	private String isbn;
	private String title;
	private String author;
	private String description;
	private int price;
	
	public NormalBook() {}
	public NormalBook(String isbn, String title, String author, String description, int price) {
		super();
		this.isbn = isbn;
		this.title = title;
		this.author = author;
		this.description = description;
		this.price = price;
	}
	public NormalBook(String isbn, String title, String author) {
		super();
		this.isbn = isbn;
		this.title = title;
		this.author = author;
	}
	
	public String getIsbn() {
		return isbn;
	}
	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	
	@Override
	public String toString() {
		return "NormalBook [isbn=" + isbn + ", title=" + title + ", author=" + author + ", description=" + description
				+ ", price=" + price + "]";
	}
	
}
package builder;

// slim 버전
public class BuilderBook {
	private String isbn;
	private String title;
	private String author;
	private String description;
	private int price;
	
	public BuilderBook() {}
	
	public static BuilderBook builder() {
		return new BuilderBook();
	}
	
	public BuilderBook isbn(String isbn) {
		this.isbn = isbn;
		return this;
	}
	
	public BuilderBook title(String title) {
		this.title = title;
		return this;
	}
	
	public BuilderBook author(String author) {
		this.author = author;
		return this;
	}
	
	public BuilderBook description(String description) {
		this.description = description;
		return this;
	}
	
	public BuilderBook price(int price) {
		this.price = price;
		return this;
	}
	
	@Override
	public String toString() {
		return "BuilderBook [isbn=" + isbn + ", title=" + title + ", author=" + author + ", description=" + description
				+ ", price=" + price + "]";
	}
	
}
package builder;

public class Board {
	private String title;
	private String content;
	private String category;
	
	public Board(Builder builder) {
		this.title = builder.title;
		this.content = builder.content;
		this.category = builder.category;
	}
	
	
	@Override
	public String toString() {
		return "Board [title=" + title + ", content=" + content + ", category=" + category + "]";
	}


	// Board 를 사용하는 쪽에 먼저 노출되어 가짜 set 작업을 수행한 후, 마지막에 build() 호출되면 진짜 Board 객체를 생성해서 return 한다.
	public static class Builder {
		private String title;
		private String content;
		private String category;
		
		public Builder title(String title) {
			this.title = title;
			return this;
		}
		
		public Builder content(String content) {
			this.content = content;
			return this;
		}
		
		public Builder category(String category) {
			this.category = category;
			return this;
		}
		
		public Board build() {
			return new Board(this);
		}
	}
}
package builder;

public class Test {

	public static void main(String[] args) {
		NormalBook nBook = new NormalBook();
		nBook.setIsbn("111");
		nBook.setTitle("제목1");

		NormalBook nBook2 = new NormalBook("222", "이길동", "제목2"); // 순서 오류
		// 적당한 filed 를 set 할 수 있는 적당한 생성자를 추가로 요구
		
		BuilderBook bBook = BuilderBook.builder()
										.title("제목2")
										.isbn("222")
										.author("이길동");
		System.out.println(bBook);
	
	
		// Board
		Board board = new Board.Builder()
								.title("제목3")
								.content("제목3")
								.category("분류1")
								.build();
		System.out.println(board);
	
	}

}

Refactoring

1. 파라미터 객체

import java.util.Date;

public class _01_Parameter_Object {
	// Old
    static class CustomerOld{
        public int getOrderAmount(Date from, Date to) { return 0; }
        
        public int getCancelAmount(Date from, Date to) { return 0; }
        
        public int getCartAmount(Date from, Date to) { return 0; }
    }
    
 // New
    static class CustomerNew{
        public int getOrderAmount(FromTo fromTo) { return 0; }
        
        public int getCancelAmount(FromTo fromTo) { return 0; }
        
        public int getCartAmount(FromTo fromTo) { return 0; }
    }
    
    static class FromTo{
    	Date from;
    	Date to;
    }
}

2. 파라미터 메소드


public class _02_Parameterize_Method {
    // Old
    static class EmployeeOld{
        public void raiseSalary10Percentage() {}        
        public void raiseSalary20Percentage() {}        
        public void raiseSalary50Percentage() {}
    }
    
    // New
    static class EmployeeNew{
        public void raiseSalaryPercentage(int percentage) {}    
    }
}

3. 객체의 전체 상태를 보존


public class _03_Preserve_Whole_Object {
    static class Today{
        int getLow() { return 0; }
        int getHigh() { return 0; }
    }
    
    static void calcLowHigh() {
        Today today = new Today();
        int low = today.getLow();
        int high = today.getHigh();
        
        checkWeatherOld(low, high);
    }
    
    static void checkWeatherOld(int low, int high) {}
    static void checkWeatherNew(Today today) {}
}

4. 여러 조건식 통합, 간소화


public class _04_Consolidate_Conditional_Expression {
	// Old
    public void registerOld(String userName, String userPassword, String userEmail) {
        if( userName.contains("aaa")) stopRegister();
        if( userPassword.contains("bbb")) stopRegister();
        if( userEmail.contains("ccc")) stopRegister();
        doRegister();
    }
    
    public void stopRegister() {}
    public void doRegister() {}
    
    // New
    public void registerNew(String userName, String userPassword, String userEmail) {
        if( registerValidate(userName, userPassword, userEmail)) doRegister();
        stopRegister();
    }
    
    public boolean registerValidate(String userName, String userPassword, String userEmail) {
    	if( userName.contains("aaa")) return false;
        if( userPassword.contains("bbb")) return false;
        if( userEmail.contains("ccc")) return false;
        return true;
    }
}

5. 중복된 조건적 코드 조각을 통합


public class _05_Consolidate_Duplicate_Conditional_Fragments {
    
    public static void sendToCustomer(int customerId, int price) {  }
    
    public static int getSaledPrice() { return 0; }
    public static int getRegularPrice() { return 0; }
    
    public static boolean isTodayInSale() { return false; }
    
    
    // Old
    public static void calculatePriceOld() {
        int customerId = 100;
        int price = 0;
        
        if( isTodayInSale() ) {
            price = getSaledPrice();
            sendToCustomer(customerId, price);
        }else {
            price = getRegularPrice();
            sendToCustomer(customerId, price);
        }
    }
    
    // New
    public static void calculatePriceNew() {
        int customerId = 100;
        int price = 0;
        
//        if( isTodayInSale() ) {
//            price = getSaledPrice();
//        }else {
//            price = getRegularPrice();
//        }
        // 3항 연산자 조건 ? _ : _
        price = isTodayInSale() ? getSaledPrice() : getRegularPrice();
        sendToCustomer(customerId, price);
    }
    
}

6. 복잡한 조건식을 간단한 부분으로 분해


public class _06_Decompose_Conditional {
    //Old
    public static int calcOld(int val1, int val2, int val3) {
        int result = 0;
        if( (val1 < 100 && val3 > 200) || val1 == 100 && val2 == 200 ) {
            result = val1*val2 - val3;
        }else {
            result = val2 + val3 - val1;
        }
        
        return result;
    }
    
    // New
    public static int calcNew(int val1, int val2, int val3) {
        int result = 0;
        if( checkValue(val1, val2, val3) ) {
            result = calcResultOne(val1, val2, val3);
        }else {
        	result = calcResultTwo(val1, val2, val3);
        }
        
        return result;
    }
    
    public static boolean checkValue(int val1, int val2, int val3) { 
        if( (val1 < 100 && val3 > 200) || val1 == 100 && val2 == 200 ) return true;
        return false;
    }
    public static int calcResultOne(int val1, int val2, int val3) {
        return val1 * val2 - val3;
    }
    public static int calcResultTwo(int val1, int val2, int val3) {
        return val2 + val3 - val1;
    }
}

7. 조건문의 조건식을 반대로 만드는 것


public class _07_Reverse_Conditional {
    public static boolean isEven(int num) { return false; }
    public static boolean isOdd(int num) { return false; }
    
    // Old
    public static void calcOld(int num) {
        if( !isEven(num) ) {
            
        }else {
            
        }
    }
    
    // New
    public static void calcNew(int num) {
        if( isOdd(num) ) {
            
        }else {
            
        }
    }
}

9.질의(쿼리)와 변경(수정)을 분리하는 것


public class _09_Separate_Query_From_Modifier {
    // Old
    public static String getUserNameChangeUserState(int userId) {
    	// 사용자 이름도 전달, 사용자의 상태값도 변경
        return "userName"; 
    }
       
    // New
    public static String getUserName() {
        return "userName"; 
    }
    
    public void changeUserState() {}
}

10. Extract_Class


public class _10_Extract_Class {
    // Old
    static class CustomerOld{
        String userName;
        // hobby
        boolean loveFootball;
        String favoriatePlayer;
    }
    
    // New
    static class CustomerNew{
        String userName; 
        Hobby hobby;
    }
    
    static class Hobby {
        boolean loveFootball;
        String favoriatePlayer;
    }
        
}

// java : Class : Customer, Order, Board ==> DB Table customer table, order table, board table

11. Extract_Interface


public class _11_Extract_Interface {
    // Old
    static class DogOld{
        void run() {}
        void eat() {}
    }
        
    static class BirdOld{
        void fly() {}
        void eat() {}
    }
    
    static class DuckOld{
        void fly() {}
        void run() {}
        void eat() {}
    }
    
    // 개별 클래스의 메소드들 중 동일한 기능을 수행하는 (설사 클래스가 서로 동일하지 않은(상송 관계에 없는)) 경우라면 인터페이스로 분리
    // eat -> Eatable, run -> Runnable, fly -> Flyable
    // New
    
    static interface Eatable { void eat(); }
    static interface Runnable { void run(); void runFaster();}
    static interface Flyable { void fly(); }
    
    static class DogNew implements Eatable, Runnable {
        public void run() {}
        public void eat() {}
		@Override
		public void runFaster() {
			// TODO Auto-generated method stub
			
		}
    }
        
    static class BirdNew implements Flyable, Eatable {
    	public void fly() {}
    	public void eat() {}
    }
    
    static class DuckNew implements Flyable, Eatable {
    	public void fly() {}
    	public  void run() {}
    	public  void eat() {}
    }
}
profile
인자약velog

0개의 댓글