AppConfig

 

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
        //return new FixDiscountPolicy()
    }

}

- @Configuration : 설정 정보라는 것을 의미

- @Bean : @Bean을 붙이면 스프링 컨테이너에 Bean으로 등록됨

 

 

public class MemberApp {

    public static void main(String[] args) {
        
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = ac.getBean("memberService", MemberService.class);

        Member member = new Member(1, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);        
    }

}

 

스프링 컨테이너

- 위에서 사용한 ApplicationContext가 스프링 컨테이너임

- ApplicationContext는 인터페이스임

- 스프링 컨테이너는 AppConfig처럼 애플리케이션의 제어권을 가지고 의존관계 주입을 해주는 역할을 함

- @Configutation이 붙은 AppConfig를 어플리케이션의 설정 정보로 활용하여 Bean을 등록하고 의존관계를 형성함

- @Bean이 붙은 메서드의 메서드 명을 Bean이름으로 등록함 (빈 이름은 각각 달라야 함)

- getBean() 메서드로 스프링 컨테이너에 등록된 Bean을 찾아올 수 있음

- getBean(빈 이름, 타입) / getBean(타입)

 

 

BeanFactory <- ApplicationContext <- AnnotationConfigApplicationContext(Java설정 클래스 사용)

                                                                  <- GeneralXmlApplicationContext(XML 설정 파일 사용)

 

BeanFactory 

- 스프링 컨테이너의 최상위 인터페이스

- Bean관리, 조회, getBean() 메서드 제공

 

ApplicationContext

- BeanFactory + 부가기능

 

 

BeanDefinition

- xml, java설정 정보를 활용하여 BeanDefinition을 만듦

- 스프링 컨테이너는  BeanDefinition을 보고 Bean을 등록함 => 스프링 컨테이너는 설정 정보가 xml인지, java인지 몰라도도 되고 BeanDefinition에만 의존함

 

제어의 역전(IoC)

- Java설정 클래스인 AppConfig가 없을 때는 클라이언트가 직접 new키워드를 사용하여 객체를 생성해서 사용함 = 클라이언트가 직접 프로그램의 플로우를 제어함

- AppConfig를 도입한 한 뒤에는, 클라이언트는 자신의 로직을 실행하지만 하고, 어떤 구현체를 사용할지, 어떤 의존관계를 가질지는 클라이언트가 아닌 외부의 AppConfig가 제어의 주도권을 가져감 = 제어의 역전

 

의존관계 주입(DI)

- 어플리케이션의 실행 시점에 외부에서 구현 객체를 생성하여, 클라이언트에 주입(생성자)하여 클라이언트와 서버 간의 의존관계가 형성되는 것

- 외부에서 의존관계를 주입해주면, 클라이언트의 코드 변경없이 프로그램의 변경이 가능함

 

컨테이너

- AppConfig 설정 클래스 처럼 제어권을 가지고, 클라이언트가 필요로 하는 구현 객체를 만들고, 의존관계를 주입해주는 것을 DI컨테이너라고 함 = Sping-Container의 역할

클라이언트가 구현에 의존하지 않고 역할에만 의존할 수 있도록 외부에서 대신 의존관계를 만들어주는 Java설정 클래스를 도입

 

AppConfig

public class AppConfig {
    
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }

    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
        //return new FixDiscountPolicy()
    }

}

 

MemberServiceImpl (생성자 추가)

ublic class MemberServiceImpl implements MemberService{

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void join(Member member) {
        memberRepository.save(member);
    }

    public Member findMember(Long id) {
        return memberRepository.findById(id);
    }
}

 

OrderServiceImpl (생성자 추가)

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        
        Member member = memberRepository.findById(memberId); // 회원정보를 조회
        int discountPrice = discountPolicy.discount(member, itemPrice); // 할인 금액을 조회

        return new Order(memberId, itemName, itemPrice, discountPrice); // 주문 생성
    }
}

 

 

- AppConfig가 클라이언트가 필요로 하는 구현체를 대신 new로 생성해서 생성자를 통해 주입해줌(의존성 주입)

- 클라이언트가 사용할 멤버 리포지토리나 할인정책을 변경하기 위해서 AppConfig만 수정하면 됨(클라이언트 수정X)

- 클라이언트는 생성자로 들어오는 구현체가 뭔지 알 필요가 없고, 이는 AppConfig에서 결정함

- 어플리케이션이 사용 영역과 구성 영역으로 분리됨

=> DIP, OCP 만족

MemberService <- MemberServiceImpl

OrderService <- OrderServiceImpl

 

MemberRepository <- MemoryMemberRepository, JdbcMemberRepository

DiscountPolicy <- FixDiscountPolicy, RateDiscountPolicy

 

 

클라이언트 -> 회원 서비스(MemberServiceImpl) -> 회원 리포지토리(MemoryMemberRepository)

 

public class MemberServiceImpl implements MemberService{
   
    private final MemberRepository memberRepository = new MemoryMemberRepository();
   
    public void join(Member member) {
        memberRepository.save(member);
    }

    public Member findMember(Long id) {
        return memberRepository.findById(id);
    }
}

회원서비스 MemberServiceImpl는 회원 리포지토리를 사용하기 위해 MemoryRepository를 구현한 MemoryMemberRepository를 직접 new로 인스턴스 만들어서 사용 => 다형성


클라이언트 -> 주문 서비스(OrderServiceImpl) -> 회원 리포지토리, 할인금액 계산

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
   
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    //private  final DiscountPolicy discountPolicy = new RateDiscountPolicy();
   
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        
        Member member = memberRepository.findById(memberId); // 회원정보를 조회
        int discountPrice = discountPolicy.discount(member, itemPrice); // 할인 금액을 조회

        return new Order(memberId, itemName, itemPrice, discountPrice); // 주문 생성
    }
}

주문 서비스 OrderServiceImpl은 회원 리포지토리, 할인금액 계산을 위해서 직접 new로 인스턴스를 만들어서 사용

=> 다형성

 

 

- 두 가지 방법 모두 역할과 구현을 잘 분리하였고, 다형성을 잘 활용하여 변경사항이 있는 경우 new코드만 변경하면 됨

 

그런데, 객체 지향 원칙

1) DIP 위반 : 클라이언트는 인터페이스에 의존해야지, 구현체에 의존하면 안 된

=> MemberServiceImpl이 구현체 클래스인 MemoryMemberRepository에 의존

=> OrderSerivceImpl이 구현체 클래스인 MemoryMemberRepository, FixDiscountPolicy에 의존

 

2) OCP 위반 : 변경에는 폐쇄, 확장에는 개방

=> 멤버 리포지토리를  Memory에서 Jdbc로 변경하기 위해서는 클라이언트 코드인 MemberServiceImpl 변경 필요

=> 할인금액 계산 방법을  Fix에서 Rate로 변경하기 위해서는 클라이언트 코드인 OrderServiceImpl 변경 필요

 

 

해결방법 : 클라이언트가 구현체가 아닌 추상(인터페이스)에 의존하도록 변경

=> 외부에서 클라이언트 MemberServiceImpl, OrderSerivceImpl에게 MemberRepository, DiscountPolicy구현체 인스턴스를 대신 생성해서 주입해주면 됨

스프링 Concept

- Java언어의 특징인 객체 지향을 극대화

- 좋은 객체 지향 애플리케이션을 개발할 수 있도록 도와줌

- 스프링의 IoC, DI는 Java의 다형성을 활용하여 역할과 구현을 편리하게 다룰 수 있도록 도와줌

 

객체 지향 프로그래밍

- 객체 지향 프로그램

= 프로그램들이 여러 객체의 모임, 각 객체는 서로 협력, 유연하고 변경이 용이(부품을 갈아 끼우듯이 개발이 가능)

 

다형성

운전자는 운전하는 방법만 알면 된다.

- 운전자는 자동자의 기능이 바뀌어도 상관없음

- 운전자는 자동차의 내부 구조도 알 필요 없음

- 운전자는 자동차가 아반떼에서 소나타로 바껴도 운전은 할 수 있음

역할과 구현으로 구분

클라이언트는 역할만 알면 된다.

- 클라이언트는 구현체의 구조가 변경되도 영향 X

- 클라이언트는 구현체의 구조를 몰라도 됨

- 클라이언트는 구현체 자체가 바뀌어도 영향 X

(* 역할 자체가 변하면 클라이언트와 서버 모두 큰 변경이 불가피)

=> 유연하고 변경이 용이

=> 확장이 가능한 설계

=> 클라이언트에 영향 X

 

역할 = 인터페이스 (MemberRepository)

구현 = 인터페이스 구현체 (MemberMemberRepository, JdbcmemberRepository)

 

 

public class MemberService {
	//private MemberRepository memberRepository = new MemoryMemberRepository();
    private MemberRepository memberRepository = new JdbcMemberRepository();
}

 

 

 

 

SOLID

SRP 단일 책임 : 하나의 클래스는 하나의 책임

OCP 개방-폐쇄 : SW구성요소는 확장에는 OPEN, 변경에는 CLOSE (새로운 기능은 인터페이스를 새로 구현해서 확장, 클라이언트의 변경 X)

LSP 리스코프 치환 : 인터페이스 규약 준수 (자동차의 액셀을 밟으면 앞으로 가야 함, 뒤로 가면 안 됨)

ISP 인터페이스 분리 : 특정한 클라이언트를 위한 여러 인터페이스가 범용 인터페이스보다 좋음 (자동차 IF -> 운전 IF, 정비 IF)

DIP 의존관계 역전 : 클라이언트는 클래스가 아닌 인터페이스에 의존해야 함 (운전자는 운전법만 알면 되고, 소나타에 대해서는 잘 알 필요 없음)

 

public class MemberService {
	//private MemberRepository memberRepository = new MemoryMemberRepository();
    private MemberRepository memberRepository = new JdbcMemberRepository();
}

이 코드는 다형성을 잘 사용하였지만 OCP와 DIP를 위반함

=> 클라이언트인 MemberService에서 MemoryMemberRepository에서 JdbcMemberRepository를 바꿔 사용하기 위해서 MemberSerivce의 변경이 필요함 (클라이언트가 구현체를 직접 고름 => 클라이언트의 변경)

 

=> 다형성으로는 SOLID원칙을 지키기가 어려움 => 스프링의 IoC, DI컨테이너가 도와줌 (클라이언트 코드 변경 없이 기능 확장)

'WEB > spring' 카테고리의 다른 글

[Spring] 어플리케이션 Java설정 클래스  (0) 2021.04.15
[Spring] Java 다형성을 활용한 설계와 한계  (0) 2021.04.15
Maven 프로젝트 생성  (0) 2020.04.28
AOP예시  (0) 2020.03.21
AOP  (0) 2020.03.21

 열거형은 관련이 있는 상수들의 집합

- 인터페이스나 클래스 내에 선언해서 관리하던 상수들을 열거형에서 관리

 

열거형 특징

- 서로 관련있는 상수 값들을 모아서 관리

- 열거형은 Class처럼 사용할 수 있음, Class의 문법을 따름

- 상속X

- 생성자는 묵시적으로 private (public으로 하면 컴파일에러)

- 열거형은 static변수 처럼 클래스 로드 시점에 생성되는 것으로 그 이후에 맘대로 생성해서 사용할 수 없음

- 열거형을 만들면 클래스 로드 시점에 하나의 인스턴스로 생성되서 singletone으로 관리됨 > 병렬환경

- 열거형 사용시 코드 리팩토링이 수월함, 상수들을 열거형에서만 관리하므로 열거형 코드만 수정하면 됨

 

enum 열거형이름 { 상수명1, 상수명2, .... }

열거형에 정의된 상수를 사용하려면 '열거형이름.상수명' 으로 사용

 

 

values() : 열거형의 모든 상수를 반환

Direction[] dArr = Direction.values();

String name() : 열거형 상수의 이름을 문자열로 반환

int ordinal() : 열거형 상수가 정의된 순서를 반환 (0부터 시작)

valueOf(Class enumType, String name) : 지정한 열거형(enumType)에서 name과 일치하는 열거형 상수를 반환

valueOf()

valurOf(String name)

 

멤버 추가

- ordinal()을 사용하면 상수가 정의된 순서에 따라 int값을 반환하지만, 이 값을 상수의 값으로 사용하는 것은 지양

- 각 상수 값을 지정할 수 있음

- 지정된 값을 저장할 수 있는 열거형의 인스턴스 변수와 생성자를 만들어 줘야함

- 인스턴스 변수는 열거형 상수의 값을 저장하기 때문에 final로 선언

- 생성자의 제어자는 묵시적으로 private임

enum Direction {
	EAST(1), SOUTH(5), WEST(-1), NORTH(10);
    
    private final int value;
    Direction(int value) {this.value = value;}   // 묵시적으로 private Direction
    
    public int getValue(){
    	return value;
    }
    
}

 

 

package enumenum;

enum Direction {
	EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4, "^"); 
	
	private final int value;
	private final String symbol;
	private static final Direction[] DIR_ARR = Direction.values(); 
	
	Direction (int value, String symbol){
		this.value = value;
		this.symbol= symbol;
	}
	
	public int getValue() {
		return value;
	}
	public String getSymbol() {
		return symbol;
	}
	
	public static Direction of(int dir) {
		if(dir < 1 || dir > 4) {
			System.out.println("Ivalid value : " + dir);
		}
		return DIR_ARR[dir - 1];
	}
	
	public Direction rotate(int num) {
		num = num % 4;		
		return DIR_ARR[(value - 1 + num) % 4];		
	}
	
	public String toString() {
		return name() + getSymbol();
	}
	
}

public class EnumEx {
	
	public static void main(String[] args) {
		
		for(Direction d : Direction.values()) {
			System.out.printf("%s=%d%n", d.name(), d.getValue());
		}
		
		Direction d1 = Direction.EAST;
		Direction d2 = Direction.of(1);
		
		System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
		System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
		
		System.out.println(Direction.EAST.rotate(1));
		System.out.println(Direction.EAST.rotate(2));		
	}
	
}

 

EAST=1
SOUTH=2
WEST=3
NORTH=4
d1=EAST, 1
d2=EAST, 1
SOUTHV
WEST<

 

참고

velog.io/@kyle/%EC%9E%90%EB%B0%94-Enum-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%ED%99%9C%EC%9A%A9

 

자바 Enum 기본 및 활용

우아하게 Enum 써 보입 시더 허허,,^^

velog.io

 

'JAVA > java' 카테고리의 다른 글

형변환  (0) 2021.02.28
Comparator, Comparable  (0) 2021.02.28
mutable과 immutable  (0) 2021.02.25
Java 날짜 유효성(포멧) 체크  (1) 2020.07.14
toString()  (0) 2020.04.20

int > String

String str = Integer.toString(i);
String str = "" + i;

 

char > int

char c = '5';
int i = c - '0';

 

String > int

int i = Integer.parseInt(str);
int i = Integer.valueOf(str).intValue();

 

double > String

String str = Double.toString(d);

 

long > String

String str = Long.toString(l);

 

float > String

String str = Float.toString(f);

 

String > double

double d = Double.valueOf(str).doubleValue();

 

String > long

long l = Long.valueOf(str).longValue();
long l = Long.parseLong(str);

 

String > float

float f = Float.valueOf(str).floatValue();

 

 

 

'JAVA > java' 카테고리의 다른 글

Enum (Enumeration, 열거형)  (0) 2021.03.04
Comparator, Comparable  (0) 2021.02.28
mutable과 immutable  (0) 2021.02.25
Java 날짜 유효성(포멧) 체크  (1) 2020.07.14
toString()  (0) 2020.04.20

 

1. 나누기

import java.util.*;

class Solution {
    public long[] solution(long n) {
        long[] answer = {};        
        List<Long> list = new ArrayList<Long>();
        
        while(n != 0) {
            long r = n % 10;
            list.add(r);
            n /= 10;
        }
        
        answer = new long[list.size()];
        for(int i = 0 ; i < list.size() ; i++) {
            answer[i] = list.get(i);
        }
        
        return answer;
    }
}

 

2. StringBuilder의 reverse() 이용

import java.util.*;

class Solution {
    public int[] solution(long n) {
        String str = String.valueOf(n);
        StringBuilder sb = new StringBuilder(str);
        sb = sb.reverse();
        
        int[] answer = new int[sb.length()];
        for(int i = 0 ; i < sb.length() ; i++) {
            answer[i] = sb.charAt(i) - '0';
        }
        return answer;
    }
}

valueOf( ~ )  : 지정된 값을 문자열로 변환하여 반환

charAt(i) - '0' 은 int를 반환

import java.util.*;

public class Solution {
    public int[] solution(int []arr) {
        int[] answer = {};
        List<Integer> list = new ArrayList<Integer>();                
        int temp = 10;        
        
        for(int i = 0 ; i < arr.length ; i++) {            
            if(arr[i] == temp){
                continue;
            }else {
                list.add(arr[i]);
                temp = arr[i];
            }            
        }
        
        answer = new int[list.size()];
        for(int i = 0 ; i < list.size() ; i++) {
            answer[i] = list.get(i);
        }
        return answer;
    }
}

class Solution {
    public String solution(String s) {
        String answer = "";
        int flag = s.length() % 2;
        int index = s.length() / 2;        
        if(flag != 0){            
            answer = s.substring(index, index+1);
        }else {
            answer = s.substring(index-1, index+1);
        }
        return answer;
    }
}