22.Java 객체지향 설계

2022. 8. 23. 17:45JAVA

2022.08.23/24.화/수


0.개발환경 설정 : adoptopen jdk 11

 

※Maven : Build Tool 

→java 파일이 여러 개이고, 특정 파일에서는 외부 라이브러리에 의존하는

   코드들이 있는 경우 컴파일러 혼자서 컴파일 불가능

∴Build Tool 사용

 

 -pom.xml -> package.json과 비슷한 역할(프로젝트의 메타정보 표기), 의존성


1.객체지향 설계(S.O.L.I.D.)

 1)SRP - Single Responsibility principle(단일 책임 원칙)

  →하나의 클래스나 메서드는 단 하나의 책임(역할)만 가져야 함

 2)OCP - Open/Closed Principle(개방/폐쇠 원칙)

 3)DIP - Dependency Inversion Principle(의존성 역전 원칙)

 

2.간단한 은행 계좌의 입출금 내역 조회 프로그램 만들기(SRP 적용)

 

-프로그램에서 등장하는 몇 가지 도메인 용어

BankTransaction : 하나의 입출금 내역

BankStatementParser : 입출금 내역 데이터 파일 처리기, 데이터 파일을 시스템에서

                                       이해할 수 있는 형태로 파싱(Parsing)해주는 역할

BankStatementAnalyzer : 입출금 내역 분석기, 파싱된 데이터를 분석,

                                           연산 처리, 결과 출력 등의 복수 역할

BankStatementProcessor: 입출금 내역 연산 처리기, 기능 별 동작을 담당하는 역할

 

-요구사항 check list

  • [ ] 입출금 내역을 담고 있는 bank-data-simple.tsv(txt) 파일을 읽고,                                                                                   총 입출금 내역을 처리한 후, 콘솔로 결과를 조회할 수 있음
  • [ ] 특정 월에 발생한 입출금 내역을 조회할 수 있음

 

1)프로토타입 만들기

package dev.bank;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.List;

import dev.bank.data.BankStatementTSVParser;

/*
	관심사
	1.입출금 내역 데이터 읽어들임(파일 입출력 부분)
	2.주어진 형식(타입)으로 읽어들인 데이터를 파싱(Parsing)
	3.입출력 내역 결과 처리 후 출력
 */

public class BankStatementAnalyzerSimple {

	//입출금 내역 파일인 bank-data-simple.txt 파일의 경로를 지정
	private static final String RESOURCES = "src/main/resources/";
	
	public static void main(String[] args) throws IOException {
		//1번째 기능 요구사항, 모든 거래내역의 합 계산하고 콘솔로 결과 조회
		final Path path = Paths.get(RESOURCES + "bank-data-simple.txt");
//		System.out.println(path);
		final List<String> lines = Files.readAllLines(path); //List<String>
		
		double total = 0d;
		//for-each 활용하여 반복문으로 총 입출금 내역 조회
		for (String line : lines) {
			final String[] columns = line.split("\t");
			final double amount = Double.parseDouble(columns[1]);
			//Integer.parseInt("100")
			total += amount;
		}
		
		System.out.println("총 사용 금액은 " + total + "입니다.");
		
		//2번째 기능 요구사항, 특정 월에는 몇 건의 입출금 내역이 발생하였는지?
		//ex) 1월 입출금 내역 조회
		System.out.println("1월 총 사용 금액은 " + findTransactionsInJanuary() + "입니다.");
	}

	//2번째 기능 요구사항, 1월 일출금 내역 조회 메서드
	public static double findTransactionsInJanuary() throws IOException {
		
		final Path path = Paths.get(RESOURCES + "bank-data-simple.txt");
//		System.out.println(path);
		final List<String> lines = Files.readAllLines(path); //List<String>
		
		//날짜 타입은 LocalDate 타입 사용
		final DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern("dd-MM-yyyy");
		
		double total = 0d;
		//반복문 활용, 특정 월에 해당하는 일출금 내역 연산처리
		for (String line : lines) {
			final String[] columns = line.split("\t");
			final LocalDate date = LocalDate.parse(columns[0], DATE_PATTERN);
			
			if(date.getMonth() == Month.JANUARY) {
				total += Double.parseDouble(columns[1]);
			}
		}
		
		return total;
	}
}

※응집도(cohesion)

코드의 품질과 관련된 특성, 클래스나 메서드의 책임, 관심사가 서로 강하게 연결, 연관되어 있는 정도

결합도(Coupling)

코드의 품질에 영향을 미치는 또 다른 요인으로 응집도와 같이 짝으로 활용됨

응집도가 클래스, 패키지, 메서드 등의 동작이 서로 얼마나 관련되어 있는지에 대한 척도라면,

결합도한 기능이나 클래스가 다른 클래스에 얼마나 의존(dependent)하고 있는지를 나타냄

따라서 결합도란 특정 클래스를 구현하는데에 얼마나 많은 클래스들을 의존하고 있는지로 가늠할 수 있음

많이 의존할 수록 변경에 용이하지 않고, 유연성이 떨어짐

 

-응집도 향상시키고, 결합도 느슨하게 만들기

1)main

package dev.bank;

import java.io.IOException;

import dev.bank.data.BankStatementCSVParser;
import dev.bank.data.BankStatementParser;

public class MainApplication {

	public static void main(String[] args) {
		final BankStatementAnalyzer analyzer = new BankStatementAnalyzer();
		
		final BankStatementParser parser = new BankStatementCSVParser();
		
		try {
			analyzer.analyze("bank-data-simple.csv", parser);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

2)BankStatementAnalyzer 클래스

package dev.bank;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Month;
import java.util.List;

import dev.bank.data.BankStatementParser;
import dev.bank.domain.BankTransaction;
import dev.bank.service.BankStatementProcessor;

public class BankStatementAnalyzer {

	private static final String RESOURCES = "src/main/resources/";

	public void analyze(String fileName, BankStatementParser parser) throws IOException {
		final Path path = Paths.get(RESOURCES + fileName);
		final List<String> lines = Files.readAllLines(path); // List<String>

		List<BankTransaction> bankTransactions = parser.parseLinesFrom(lines);
		
		BankStatementProcessor processor = new BankStatementProcessor(bankTransactions);
		
		collectSummary(processor);
	}
	
	private static void collectSummary(BankStatementProcessor processor) {
		System.out.println("총 입출금 내역은 : " + processor.calculateTotalAmount());
		System.out.println("1월 총 입출금 내역 리스트는 : " + processor.calculateTotalInMonth(Month.JANUARY));
		System.out.println("Salary 카테고리의 내역은 : " + processor.calculateTotalByCategory("Salary"));
	}
}

3)BankTransaction(도메인 객체, 가공된 객체)

package dev.bank.domain;

import java.time.LocalDate;

//하나의 일출금 내역
public class BankTransaction {
	private LocalDate date;		//입출금 날짜
	private double amount;		//입출금 금액
	private String description; //거래처
	
	public BankTransaction(LocalDate date, double amount, String description) {
		this.date = date;
		this.amount = amount;
		this.description = description;
	}

	public LocalDate getDate() {
		return date;
	}

	public double getAmount() {
		return amount;
	}

	public String getDescription() {
		return description;
	}

	@Override
	public String toString() {
		return "BankTransaction [date=" + date + ", amount=" + amount + ", description=" + description + "]";
	}
}

4)BankStatementParser 인터페이스/클래스(파일 -> BankTransaction로 Parse)

package dev.bank.data;

import java.util.List;

import dev.bank.domain.BankTransaction;

public interface BankStatementParser {
	BankTransaction parseFrom(String line);
	List<BankTransaction> parseLinesFrom(List<String> lines);
}
package dev.bank.data;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import dev.bank.domain.BankTransaction;

//다른 문제 구현에도 파싱 로직을 활용할 수 있도록 
//TSV 파싱로직을 새로운 클래스로 분리
public class BankStatementTSVParser implements BankStatementParser {
	final DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern("dd-MM-yyyy");
	
	public BankTransaction parseFrom(final String line) {
		String[] columns = line.split("\t");
		
		final LocalDate date = LocalDate.parse(columns[0], DATE_PATTERN);
		final double amount = Double.parseDouble(columns[1]);
		final String description = columns[2];
		
		return new BankTransaction(date, amount, description);
	}
	
	//lines를 인수로 받아서 한줄 씩 파싱 후, 리스트에 추가
	public List<BankTransaction> parseLinesFrom(List<String> lines) {
		List<BankTransaction> list = new ArrayList<BankTransaction>();
		for (String line : lines) {
			list.add(parseFrom(line));
		}
		return list;
	}
}
package dev.bank.data;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import dev.bank.domain.BankTransaction;

//CSV파일에 대한 파싱 요구사항에 따른 새로운 csv parser 구현
public class BankStatementCSVParser implements BankStatementParser {
	final DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern("dd-MM-yyyy");
	
	public BankTransaction parseFrom(final String line) {
		String[] columns = line.split(",");
		
		final LocalDate date = LocalDate.parse(columns[0], DATE_PATTERN);
		final double amount = Double.parseDouble(columns[1]);
		final String description = columns[2];
		
		return new BankTransaction(date, amount, description);
	}
	
	//lines를 인수로 받아서 한줄 씩 파싱 후, 리스트에 추가
	public List<BankTransaction> parseLinesFrom(List<String> lines) {
		List<BankTransaction> list = new ArrayList<BankTransaction>();
		for (String line : lines) {
			list.add(parseFrom(line));
		}
		return list;
	}
}

5)BankStatementProcessor클래스 - Service(비즈니스 로직)

package dev.bank.service;

import java.time.Month;
import java.util.ArrayList;
import java.util.List;

import dev.bank.domain.BankTransaction;

//입출금 관련 도메인(비즈니스) 로직을 처리하는 클래스
public class BankStatementProcessor {
	//Processor 클래스에서 List<BankTransaction>를 필드로 가지고 있도록
	private final List<BankTransaction> bankTransactions;
	
	//생성자를 통해서 bankTransactions 초기화
	public BankStatementProcessor(List<BankTransaction> bankTransactions) {
		this.bankTransactions = bankTransactions;
	}

	// 전체 입출금 내역을 조회하는 메서드
	public double calculateTotalAmount() {
		double total = 0d;
		for (BankTransaction bankTransaction : bankTransactions) {
			total += bankTransaction.getAmount();
		}
		return total;
	}
	
	// 특정 월의 거래 내역 리스트를 조회하는 메서드
	public List<BankTransaction> calculateTotalInMonth(Month month) {
		final List<BankTransaction> bankTransactionsInMonth = new ArrayList<>();
		for (BankTransaction bankTransaction : bankTransactions) {
			if(bankTransaction.getDate().getMonth() == month) {
				bankTransactionsInMonth.add(bankTransaction);
			}
		}
		return bankTransactionsInMonth;
	}
	
	//거래처(category) 별 총 일출금 금액을 조회할 수 있는 조회할 수 있는 메서드(새롭게 추가)
	public double calculateTotalByCategory(String category) {
		double total = 0d;
		for (BankTransaction bankTransaction : bankTransactions) {
			if(category.equals(bankTransaction.getDescription())) {
				total += bankTransaction.getAmount();
			}
		}
		return total;
	}
}

 

'JAVA' 카테고리의 다른 글

24.Java Spring/IoC/DI/객체지향설계원칙  (0) 2022.08.29
23.Java JPA  (0) 2022.08.25
21.파일 업로드  (0) 2022.08.11
20.Web화면 재사용/EL/JSTL  (0) 2022.08.10
19.mybatis Web/필터/세션관리  (0) 2022.08.09