package section1;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class code03 {
	
	static Person1 members[]= new Person1[100];
	static int n = 0;

	public static void main(String[] args) {
		try {
			Scanner in = new Scanner(new File("data.txt"));
			while(in.hasNext()) {
				String name = in.next();
				String number = in.next();
				
				members[n] = new Person1();
				members[n].name=name;
				members[n].number=number;
				n++;
			}
			
			
			in.close();
		} catch (FileNotFoundException e) {
			System.out.println("No file");
		}
		
		bubleSort();
		
		for(int i=0; i<n; i++) {
			System.out.println(members[i].name + " "+ members[i].number);
		}
	}

	private static void bubleSort() {
		
		for(int i=n-1;i>0;i--) {
			for(int j =0; j<i; j++) {
				if(members[j].name.compareTo(members[j+1].name) > 0 ) {
					Person1 tmp = members[j];
					members[j] = members[j+1];
					members[j+1] = tmp;
				}
			}
		}
		
	}

}

저번시간의 코드를 살짝수정하여 버블정렬하는 코드를 작성했다

클래스를 사용하기 전에는 정렬시에 이름따로 번호따로 정렬을 하는 코드를 짰었는데

클래스를 사용하니 객체배열 각각의 참조변수에 주소값을 바꿔주어 이용하여 관련있는

데이터를 한번에 가르키게 될 수 있게 되엇다. 

 

new명령어로 만들게 되면 고유한 이름을 가질 수 없기때문에

객체만 있어서는 객체를 사용할 수 없다 그래서 참조 변수가 필요하다

 

클래스, 참조변수, 객체에 대한 이해가 잘 되게 되었다!

 


인덱스 메이커 프로그램의 수정

어떠한 텍스트 파일이 있다면 그 파일안에의 단어추출과 단어의 등장 빈도를 구해주는 프로그램을

클래스를 사용하여 리팩토링 해보았다!

 

 

package section1;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

public class IndexMaker {
	
	static Item items[] = new Item[100000];
	static int n = 0;
	
	public static void main(String[] args) {
		
		Scanner kb = new Scanner(System.in);
		while(true) {
			System.out.print("$ ");
			String command = kb.next();
			if(command.equals("read")) {
				String filename =kb.next();
				makeIndex(filename);
			}
			else if(command.equals("find")) {
				String str = kb.next();
				int index = findWord(str);
				if(index > -1) {
					System.out.println("The word " + items[index].word +" appears " + items[index].count + " times.");
				}
				else
					System.out.println("The word " + str +" dose not appears ");
			}
			else if(command.equals("saveas")) {
				String filename = kb.next();
				saveAs(filename);
			}
			else if(command.equals("exit")) {
				break;
			}
			
			
		}
		kb.close();
		
		for(int i=0; i<n; i++) {
			System.out.println(items[i].word+" "+items[i].count);
		}
	}
	static void makeIndex(String filename){
		
		try {
			Scanner inFile = new Scanner(new File(filename));
			while(inFile.hasNext()) {
				String str = inFile.next();
				
				String trimed = triming(str);
				if(trimed != null) {
					String t = trimed.toLowerCase();
					addWord(t);
				}
			}
			inFile.close();
		} catch (FileNotFoundException e) {
			System.out.print("No File");
			return;
		}
	}
	static String triming(String str) {
		
		int i =0, j = str.length()-1;
		while(i <str.length() && !Character.isLetter(str.charAt(i)))
			i++;
		while(j<str.length() && !Character.isLetter(str.charAt(j)))
			j--;
		
		if(i>j)
			return null;
		return str.substring(i,j+1);
	}
	static void addWord(String str) {
		int index = findWord(str);
		if(index != -1) {
			items[index].count++;
		}
		else {
			int i =n-1;
			for(;i>=0  && items[i].word.compareToIgnoreCase(str)>0;i--) {
				
				items[i+1] = items[i];
				i--;
			}
			items[i+1] = new Item();
			items[i+1].word = str;
			items[i+1].count =1;
			n++;
		}
		
	}
	static int findWord(String str) {
		for(int i=0;i<n;i++) 
			if(items[i].word.equalsIgnoreCase(str)) {
				return i;
			}
		return -1;
	}
	static void saveAs(String fileName) {
		try {
			PrintWriter outFile = new PrintWriter(new FileWriter(fileName));
			for(int i =0;i<n;i++) {
				outFile.println(items[i].word + " " + items[i].count);
			}
			outFile.close();
		} catch (IOException e) {
			System.out.println("save failed");
			return;
		}
	}
}

 중요한 것은 역시 객체안에 내용을 건들때는 항상new를 통해 생성을 해주어야한다는 것을

자꾸 까먹게 되니 계속 생각을 해야하는것 같다!

 

 

'알고리즘 with 자바 > 자료구조' 카테고리의 다른 글

클래스 ,객체, 참조변수 5  (0) 2021.06.23
클래스, 객체, 참조변수 4  (0) 2021.06.22
클래스, 객체, 참조변수 2  (0) 2021.06.21
클래스 , 객체, 참조변수 1  (0) 2021.06.21
문자열 다루기 2  (0) 2021.06.19

이번시간에는 자바코드로 직접 스프링 빈을 등록하는 방법에 대해 알아보았다!

package hello.hellospring;

import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {
    
    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }
    
    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

SpringConfig클래스를 생성하고

@Configuration애너테이션을 주고

 

MemberService, MemberRepository를 @Bean애너테이션을 주게되면

등록이 된다!  MemberService는 MemberRepository를 생성자로 받아서 생성하고

MemberRepository는 인터페이스이기때문에 MemoryMemberRepository구현체를 

new로 생성 후 반환해준다!

 

컨트롤러는 어쨋든 스프링이 어차피 관리하는 것이기때문에 따로 수정이 필요없다!

 

이것이 두번째 자바코드로 직접 스프링 빈 등록하는 방법이다.

 

참고: 자바코드로 설정하지 않고 과거에는 XML이라는 문서로 설정을 하였다.

        지금은 거의 사용하지 않고 자바코드로 설정을한다.

 

DI에는 생성자 주입, 필드 주입, setter주입 3가지 방법이있다.

필드 주입은 나중에 변경을 할 수 없게되고

setter주입은 public하게 노출이 되게 된다.

 

실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.

그리고 정형화 되지않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.

(현재 DB가 정해지지 않은 가상의 시나리오가 있기때문에 스프링 빈으로 등록하는 방법을 사용한다)

이번 시간에는 스프링 빈을 등록하고 의존관계 설정을 해보았다.

화면을 붙이고 싶은데 그럴려면 컨트롤러와 뷰템플릿이 필요하다!

MemberController가 MemberService를 통해 가입하고

MemberService를 통하여 조회하여야한다 그럴때 의존관계가 필요하다 

package hello.hellospring.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberController {

}

컨트롤러 패키지 하위에 MemberController를 생성하고 @Controller애너테이션을 주면

기능은 아무렇지 않지만 어떤 일이벌어지냐면

스프링 뜰때 스프링 컨테이너라는 통이 생기는데 거기에 @Controller애너테이션이 있으면

MemberController 객체를 생성하여 스프링에 넣어두고 스프링이 관리를 한다!

 

package hello.hellospring.controller;

import hello.hellospring.service.MemberService;
import org.springframework.stereotype.Controller;

@Controller
public class MemberController {

    private final MemberService memberService = new MemberService();
}

멤버 서비스를 가져다가 써야해서 new로 생성하여 쓸 수 도 있다 그런데!

스프링이 관리를 하게 되면 다 스프링 컨테이너에 등록을 하여 받아 쓰도록 바꿔야한다

왜냐하면 new를 쓰게되면 다른 컨트롤러에서 멤버 서비스를 가져다가 쓰게 되는데는 문제가 생긴다

그렇기에

여러개의 인스턴스를 생성할 필요가 없이 하나만 생성하고 공용으로 쓰면 된다!

스프링 컨테이너한테 등록을 하고 쓰면 하나만 등록하게 된다 

package hello.hellospring.controller;

import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class MemberController {


    private final MemberService memberService;

    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }
}

생성자로 연결해주고 Autowired애너태이션을 주면 된다.

생성자에 Autowired라고 되어있으면

스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.

이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection)

의존성 주입이라 한다. 하지만! 코드를 실행시키면

Parameter 0 of constructor in hello.hellospring.service.MemberService required a bean of type 'hello.hellospring.repository.MemberRepository' that could not be found.

 

MemberService를 찾을 수 없다고한다

출처:https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/lecture/49586?tab=note&speed=0.75&mm=null

MemberService는 순수한 자바클래스 이기때문에 스프링이 알 수 있는 방법이 없다!

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class MemberService {
    
    private final MemberRepository memberRepository;

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

    public Long join(Member member) {
        //같은 이름이 있는 중복회원이 안된다는 시나리오
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
            .ifPresent((m->{
                throw new IllegalStateException("이미 존재하는 회원입니다");
            }));
    }

    public List<Member> findMembers(){
       return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}

메서드 위에 @Service애너테이션을 주게 되면 스프링이 올라올때 스프링 컨테이너에 memberService를 등록해준다

 

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class MemoryMemberRepository implements MemberRepository {

    private static Map<Long, Member> store = new HashMap<>();
    private static Long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(),member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }


    public void clearStore(){
        store.clear();
    }
}

MemoryMemberRepository 또한 @Repository 애너테이션을 해주어야 한다

이것이 정형화된 패턴이다.

컨트롤러 서비스 리포지토리.

컨트롤러를 통하여 외부 요청을 받고

서비스에서 비즈니스 로직을 만들고

리포지토리에서 데이터를 저장하고 하는것이 정형화된 패턴이다.

 

이렇게 애너테이션을 해주면 스프링이 컨트롤러 서비스 리포지토리를 가지고 올라온다!

 

MemberService 생성자에 Autowired 애너테이션을 주면 3개가 연결된다!

 

스프링 빈을 등록하는데에는 2가지 방법이있다.

여기까지 한 것이 컴포넌트 스캔과 자동 의존관계 설정 방식이다.

스프링이 올라올때 컴포넌트와 관련된 애너테이션이 있으면 걔들은 객체를 하나씩 생성하여

스프링 컨테이너에 등록을 한다 Autowired는 연관관계를 연결해주고 그래서 MemberController가 

MemberService를 쓸 수 있게해주고 MemberService가 MemberRepository를 쓸 수 있게 해주게

된다!

 

참고로 스프링은 스프링 컨테이너에 스프링 빈을 등록할때, 기본으로 싱글톤으로 등록한다.(유일하게 하나만

등록하여 공유한다.) 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다. 서렂ㅇ으로 싱글톤이 아니게 할 수는

있지만 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.

 

자바 코드로 직접 스프링 빈 등록하는 방법도 있다.

전 시간에 만들었던 회원 서비스를 Junit을 이용하여 테스트를 해보았다!

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;
    
    @BeforeEach
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

    @AfterEach
    public void afterEach() {
        memberRepository.clearStore();
    }

    @Test
    void 회원가입() {
        //given
        Member  member = new Member();
        member.setName("hello");
        //when
        Long saveId = memberService.join(member);
        //then
        Member findMember = memberService.findOne(saveId).get();
        Assertions.assertThat(member.getName()).isEqualTo(findMember.getName());
    }

    @Test
    public void 중복_회원_예외(){
        //given
        Member member1 =new Member();
        member1.setName("Spring");

        Member member2 =new Member();
        member2.setName("Spring");

        //when
        memberService.join(member1);
        IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));

        Assertions.assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다");
        /*    try {
            memberService.join(member2);
            fail();
        }catch(IllegalStateException e){
            Assertions.assertThat(e.getMessage()).isEqualTo("");
        }*/
        //then
    }

    @Test
    void findMembers() {
    }

    @Test
    void findOne() {
    }
}

테스트 같은 경우에는 외국인들과 일하는 것이 아니라면 메서드명을 한글로 해도 상관이없다!

또한 given , when, then 주석을 이용하여 과정을 정리하는 것이 도움이 된다!

 

 

일단 회원가입 같은 경우

멤버 객체를 만들고 이름을 넣어준 뒤 saveId 변수에 id 값을 받고

findMember메서드를 이용하여 나온Name 값과 현재 Name값이 같은지 확인한다!

 

하지만 이 경우 중복회원가입에 대한 예외가 터지는지를 알 지못하기 때문에 

테스트를 하나 더작성하였다 

이름은 중복_회원_예외!

 

두 객체를 생성후 Name값을 같게 준다!

join을 이용하여 1번 멤버를 가입시킨다

2번 멤버를 가입시킬때 예외가 뜬다면 이미 존재하는 회원입니다 라는

문구가 나오는데 그문구와 같다면 중복회원을 막는것이 성공한 것이다!

 

그리고 또한 메모리값을 초기화 해주기 위해 클리어 해주는 afterEach()를 생성하고

    private final MemberRepository memberRepository;

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

MemberService클래스에서의 수정일 필요하다 이유는 MemberService의 테스트를 하는데 

다른 인스턴스 이기 때문이다!!

 

그리고 동작하기 전에 넣어주어야 하기때문에 

BeforeEach

를 이용하여 선언만 해준 코드에다가

객체를 생성한다 이렇게하면 테스트 실행할때 마다

그 전에 실행이 된다!

이것은 직접 멤버서비스 입장에서는 직접 new하지않고 외부에서 넣어주게 되는데

이것을 DI(의존성 주입) 이라고한다!

클래스와 객체


int[] numbers = new int[8];

 

정수형 배열 numbers 선언!

 

int는 프리미티브 타입이다..

int형 배열은??? 프리미티브 타입이 아니다!

즉 참조변수이다! 다른 어떤 객체의 주소를 저장하는 변수!!

 

각각의 칸은 정수형 변수이다! 즉 프리미티브 타입의 변수이다.


Person1[] members = new Person1[8]

 

Person1 타입의 배열 members 선언!

배열의 각각 칸 까지도 프리미티브 타입이 아님!

 


package section1;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class code02 {
	
	static Person1 members[]= new Person1[100];
	static int n = 0;

	public static void main(String[] args) {
		try {
			Scanner in = new Scanner(new File("data.txt"));
			while(in.hasNext()) {
				String name = in.next();
				String number = in.next();
				
				members[n] = new Person1();
				members[n].name=name;
				members[n].number=number;
				n++;
			}
			
			
			in.close();
		} catch (FileNotFoundException e) {
			System.out.println("No file");
		}
		
		for(int i=0; i<n; i++) {
			System.out.println(members[i].name + " "+ members[i].number);
		}
	}

}

객체배열 예제! 반복문 내에서 객체배열은 각각의 변수들이 프리미티브 타입이 아니기때문에 

new 연산자가 필요하다!

 

 

 

 

 

 

 

'알고리즘 with 자바 > 자료구조' 카테고리의 다른 글

클래스, 객체, 참조변수 4  (0) 2021.06.22
클래스, 객체, 참조변수 3  (0) 2021.06.22
클래스 , 객체, 참조변수 1  (0) 2021.06.21
문자열 다루기 2  (0) 2021.06.19
문자열 다루기  (0) 2021.06.19

서로 관련있는 데이터들을 하나의 단위로 묶어두면 편할 것이다! 라는 생각에

클래스라는 개념이 등장하는 가장 기본적인 이유이다.

 

클래스는 하나의 타입이다.

(사용자 정의 타입) 

 

package section1;

public class Person1 {
	
	public String name;  //field, data memeber
	public String number;
}

Person1 클래스 생성

 

package section1;

public class code01 {

	public static void main(String[] args) {

		Person1 first = new Person1();  //object
		
		first.name="John";
		first.number="01012345678";
		System.out.println("Name : " + first.name + "," + "Number : " + first.number);
		
		Person1[] members= new Person1[10];
		members[0] = first;
		members[1] = new Person1();
		members[1].name = "David";
		members[1].number = "01098765432";
		
		System.out.println("Name : " + members[1].name + "," + "Number : " + members[1].number);

		
	}

}

Person1타입 으로 first변수를 만들고 

직접 데이터를 넣어주어서 확인해보고

 

Person1타입 배열 members를 만들고

데이터를 넣어줬다!

 

first 라는 변수에는 new로 만든 객체의 주소가 저장이된다

 

Person1 first; 

   - 아직 객체는 생성되지 않고 변수 first만 만들어진다 이때 변수의 값은 null이다

 

first = new Person();

   - new Person1() 명령에 의해 객체가 만들어지고 first에 그 주소를 저장한다.(참조하게됨)

 

first.name = "John"; first.number = "01023456";

   -first가 참조하고 있는 Person1 타입의 객체의 name, number라는 이름의 필드에 데이터를 저장한다.

 

모든 프리미티브 타입의 변수는 보통 변수이다.

즉 변수 자체에 값이 저장된다.

 

프리미티브 타입이 아닌 모든 변수는 참조변수이다.

즉 실제 데이터가 저장될 "객체"는 new명령으로 따로 만들어야 하고, 참조변수에는 그 객체의 주소를 저장한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

지난 시간의 수정해야할 점에대하여!

 

-소수점, 쉼표 등의 특수기호가 단어에 포함된다.

-숫자 등이 단어로 취급된다.

-단어들이 알파벳 순으로 정렬되면 좋겠다.

 


String 클래스 기본 메서드

 

문자열 동일성 검사

boolean

equals(String)

 

문자열 사전식 순서

int

compareTo(String)

 

문자열 길이

int

length()

 

특정 위치의 문자

char

charAt(int)

 

지정한 문자의 위치검색

int

indexOf(char)

 

지정된 범위의 부분 문자열

String

substring(int,int)

 

package test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

public class code23 {

	static String [] words = new String[100000];
	static int[] count = new int[100000];
	static int n = 0;
	
	public static void main(String[] args) {
		
		Scanner kb = new Scanner(System.in);
		while(true) {
			System.out.print("$ ");
			String command = kb.next();
			if(command.equals("read")) {
				String filename =kb.next();
				makeIndex(filename);
			}
			else if(command.equals("find")) {
				String str = kb.next();
				int index = findWord(str);
				if(index > -1) {
					System.out.println("The word " + words[index] +" appears " + count[index] + " times.");
				}
				else
					System.out.println("The word " + str +" dose not appears ");
			}
			else if(command.equals("saveas")) {
				String filename = kb.next();
				saveAs(filename);
			}
			else if(command.equals("exit")) {
				break;
			}
			
			
		}
		kb.close();
		
		for(int i=0; i<n; i++) {
			System.out.println(words[i]+" "+count[i]);
		}
	}
	static void makeIndex(String filename){
		
		try {
			Scanner inFile = new Scanner(new File(filename));
			while(inFile.hasNext()) {
				String str = inFile.next();
				
				String trimed = triming(str);
				if(trimed != null) {
					String t = trimed.toLowerCase();
					addWord(t);
				}
			}
			inFile.close();
		} catch (FileNotFoundException e) {
			System.out.print("No File");
			return;
		}
	}
	static String triming(String str) {
		
		int i =0, j = str.length()-1;
		while(i <str.length() && !Character.isLetter(str.charAt(i)))
			i++;
		while(j<str.length() && !Character.isLetter(str.charAt(j)))
			j--;
		
		if(i>j)
			return null;
		return str.substring(i,j+1);
	}
	static void addWord(String str) {
		int index = findWord(str);
		if(index != -1) {
			count[index]++;
		}
		else {
			int i =n-1;
			for(;i>=0  && words[i].compareToIgnoreCase(str)>0;i--) {
				words[i+1] = words[i];
				count[i+1] = count[i];
			}
			words[i+1] = str;
			count[i+1] =1;
			n++;
		}
		
	}
	static int findWord(String str) {
		for(int i=0;i<n;i++) 
			if(words[i].equalsIgnoreCase(str)) {
				return i;
			}
		return -1;
	}
	static void saveAs(String fileName) {
		try {
			PrintWriter outFile = new PrintWriter(new FileWriter(fileName));
			for(int i =0;i<n;i++) {
				outFile.println(words[i] + " " + count[i]);
			}
			outFile.close();
		} catch (IOException e) {
			System.out.println("save failed");
			return;
		}
	}
}

addWord findWord makeIndex 함수들을 수정하여 완료하였다.

 

출력파일!!

소문자로 변경되었고 불필요한 특수문자와 단어순서대로 정렬되었다.

문자열과 리스트 다루기

 

-인덱스 메이커

1.입력으로 하나의 텍스트 파일을 읽는다.

2.텍스테 파일에 등장하는 모든 단어들의 목록을 만들고, 각 단어가 텍스트 파일에 등장하는

  횟수를 센다. 단어 개수는 100,000개 이하라고 가정

3.사용자가 요청하면 단어 목록을 하나의 파일로 저장한다.

4.사용자가 단어를 검색하면 테스트파일에 몇번 등장하는지 출력한다.

 

package test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

public class code22 {

	static String [] words = new String[100000];
	static int[] count = new int[100000];
	static int n = 0;
	
	public static void main(String[] args) {
		
		Scanner kb = new Scanner(System.in);
		while(true) {
			System.out.print("$ ");
			String command = kb.next();
			if(command.equals("read")) {
				String filename =kb.next();
				makeIndex(filename);
			}
			else if(command.equals("find")) {
				String str = kb.next();
				int index = findWord(str);
				if(index > -1) {
					System.out.println("The word " + words[index] +" appears " + count[index] + " times.");
				}
				else
					System.out.println("The word " + str +" dose not appears ");
			}
			else if(command.equals("saveas")) {
				String filename = kb.next();
				saveAs(filename);
			}
			else if(command.equals("exit")) {
				break;
			}
			
			
		}
		kb.close();
		
		for(int i=0; i<n; i++) {
			System.out.println(words[i]+" "+count[i]);
		}
	}
	static void makeIndex(String filename){
		
		try {
			Scanner inFile = new Scanner(new File(filename));
			while(inFile.hasNext()) {
				String str = inFile.next();
				addWord(str);
			}
			inFile.close();
		} catch (FileNotFoundException e) {
			System.out.print("No File");
			return;
		}
	}
	static void addWord(String str) {
		int index = findWord(str);
		if(index != -1) {
			count[index]++;
		}
		else {
			words[n] = str;
			count[n] =1;
			n++;
		}
	}
	static int findWord(String str) {
		for(int i=0;i<n;i++) 
			if(words[i].equalsIgnoreCase(str)) {
				return i;
			}
		return -1;
	}
	static void saveAs(String fileName) {
		try {
			PrintWriter outFile = new PrintWriter(new FileWriter(fileName));
			for(int i =0;i<n;i++) {
				outFile.println(words[i] + " " + count[i]);
			}
			outFile.close();
		} catch (IOException e) {
			System.out.println("save failed");
			return;
		}
	}
}

output 파일이 생성되었다.

 

모든 기능들을 하나의 함수에서 구현하려하지않고 기능별로 

함수를 만드는 것이 매우중요하다는걸느낌...

회원 서비스는 

회원 리포지토리와 도메인을 활용하여 

실제 비즈니스 로직을 작성하는 부분이다.

 

service 패키지 만든 후

MemberService 클래스 생성

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;

import java.util.List;
import java.util.Optional;

public class MemberService {
    
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    
    public Long join(Member member) {
        //같은 이름이 있는 중복회원이 안된다는 시나리오
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
            .ifPresent((m->{
                throw new IllegalStateException("이미 존재하는 회원입니다");
            }));
    }

    public List<Member> findMembers(){
       return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}

 

join 메서드는 

memberRepositoy 에서 save만 불러오면된다!

하지만 이름 중복이 안된다는 가상의 시나리오에 의해

validateDuplicateMember 메서드를 생성하여 

findByname으로 찾아보고 있다면 이미 존재하는 회원이라는 문구를 준다!

 

findMembers는(전체회원조회)

단순히 findAll을 반환한다!

 

findOne은

id값으로 찾은 멤버 id를 반환한다!

 

서비스 클래스는 비즈니스에 의존적으로 설계한다.(비즈니스 처리 역할)

리포지토리는 기계적 개발스럽게 용어선택 등 설계함!

 

이전에 만든 클래스가 내가 원하는대로 정상적으로 동작할까?

검증하는 방법에 대해 배워보았다 

코드를 코드로 검증하기!


  JUnit프레임워크로 테스트 실행하기!

package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.*;

public class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach() {
        repository.clearStore();
    }
    	//하나의 메서드 테스트가 끝나면 데이터를 지워주는 메서드

    @Test
    public void save() {
        Member member = new Member();
        member.setName("Spring");
			//멤버클래스의 Name을 스프링으로 설정
        repository.save(member);
			//MemoryMemberRepository클래스로 만든 repository에 save함수에 만든 member를 매개변수로줌
        Member result = repository.findById(member.getId()).get();
        	//result라는 객체생성 하고 findById에 member의 id로 준다! get은 옵셔널 타입에서 꺼내는 함수
        assertThat(member).isEqualTo(result);
        	//두 값이 같으면 참이다
    }
    
    

    @Test
    public void findByName(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);
       		//member1 객체 생성 후 save!

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);
        	//member2 객체 생성 후 save!

        Member result = repository.findByName("spring1").get();
        	// result 객체 생성 후 spring1찾아 받는다
        assertThat(result).isEqualTo(member1);
        	// member1 과 result가 동일한지 확인
    }

    @Test
    public void findAll(){
        Member member1 = new Member();
        member1.setName("spring1");
        repository.save(member1);
        //member1 객체 생성 후 save

        Member member2 = new Member();
        member2.setName("spring2");
        repository.save(member2);
		//member2 객체 생성 후 save
        
        List<Member> result = repository.findAll();
			//result에 다 넣어줌
        assertThat(result.size()).isEqualTo(2);
        	//2개 생성했으니 사이즈와 같아야하는지 비교하는코드
    }
}

이전에 작성했던 MemoryMemberRepository에

public void clearStore(){
store.clear();
}

이코드를 추가해주며 afterEach()

메서드를 생성할 수 있다!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'웹프로그래밍 > Spring 입문' 카테고리의 다른 글

9. 회원 서비스 테스트  (0) 2021.06.21
8. 회원 서비스 개발  (0) 2021.06.19
6. 회원 도메인과 리포지토리 만들기  (0) 2021.06.17
5. 비즈니스 요구사항 정리  (0) 2021.06.17
4. API  (0) 2021.06.17

+ Recent posts