Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2주차] 객체지향 코드 연습(starshape7) #1

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
74 changes: 74 additions & 0 deletions src/Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import java.math.BigDecimal;

public class Account
{
private String accountType;
private String accountNum;
private String owner;
private BigDecimal amount;
private boolean activated;

public Account(String accountType, String accountNum, String owner, BigDecimal amount)
{
this.accountType = accountType;
this.accountNum = accountNum;
this.owner = owner;
this.amount = amount;
this.activated = true;
}

public String getAccountType() {return accountType;}
public void setAccountType(String accountType) {this.accountType = accountType;}
public String getAccountNum() {return accountNum;}
public void setAccountNum(String accountNum) {this.accountNum = accountNum;}
public String getOwner() {return owner;}
public void setOwner(String owner) {this.owner = owner;}
public BigDecimal getAmount() {return amount;}
public void setAmount(BigDecimal amount) {this.amount = amount;}
public boolean isActivated() {return activated;}
public void setActivated(boolean activated) {this.activated = activated;}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드 컨벤션에 대하여 알아보고 적용하여 읽기 쉽고 관리하기 쉬운 코드에 대해 고민해보아요

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getter와 setter를 남발하는 것은 좋지 않습니다. 객체지향의 장점은 캡슐화에 있습니다. 즉, 외부에 객체의 구체적인 정보를 은닉하는 것에 중점을 둬야합니다. 특히 객체 내부에서 setter를 사용하고있는데 외부가 아닌 내부에서 변경되는 값인데 setter를 사용하는 이유가 무엇인가요? 접근 제어자의 역할에 대해서도 고민해봅시다.

public String getAccountInfo()
{
// 계좌 정보를 문자열로 표현하여 반환
return "*************************\n" +
"Owner : " + owner + "\n" +
"Account Type : " + accountType + "\n" +
"Account Number : " + accountNum + "\n" +
"Asset : ₩" + amount + "\n" +
"Activated : " + (activated ? "Yes" : "No");
}
//출금
public void withdrawal(BigDecimal amount)
{
if(getAmount().compareTo(amount) >= 0)
{
setAmount(getAmount().subtract(amount));
System.out.println("WithDraw Finish! your Amount : ₩" + getAmount());
}
else
{
System.out.println("Failed WithDraw!");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외 발생을 통해 처리하는 것이 좋습니다

}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

출금, 입금, 송금 등의 책임이 Account 객체보다는 AccountService에 있다고 보는 것이 더 좋을 것 같습니다. Account는 자신의 계좌의 출금할 만큼 돈이 있는 지 등과 같은 역할을 하는 것이 좋을 것 같습니다. 즉, 객체의 책임에 대하여 고민해보고 분리해봅시다.
어떤 객체가 해야할 일을 다른 객체가 대신 해주고 있는데 객체의 구조가 변경된다면 그 객체의 일을 대신 해주는 모든 코드를 수정해야 합니다. 책임 분리합시다.

//입금
public void deposit(BigDecimal amount)
{
setAmount(getAmount().add(amount));
System.out.println("Deposite Finish! Your Amount : ₩" + getAmount());
}
//송금
public void transfer(Account receiver, BigDecimal amount)
{
if(getAmount().compareTo(amount) >= 0)
{
setAmount(getAmount().subtract(amount));
receiver.setAmount(getAmount().add(amount));
System.out.println("Sender : " + getOwner() + ", Receiver : " + receiver.getOwner());
System.out.println("Transfer Finish! Send ₩" + amount + " To " + receiver.getOwner());
}
else
{
System.out.println("Failed Transfer From" + this.getOwner() + " To " + receiver.getOwner() + " amount " + amount);
}
}
}
19 changes: 19 additions & 0 deletions src/AccountInterestRate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import java.math.BigDecimal;

public class AccountInterestRate implements InterestCalculator
{
@Override
public BigDecimal getInterest(BigDecimal amount)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한 개의 메소드에서 여러 개 책임을 갖고 있는 것 같습니다.
인스턴스 생성, 비교 및 검증, 값 반환 의 책임을 지고 있는 것 같네요. 분리해주는 게 좋을 것 같습니다

{
BigDecimal overTenM= new BigDecimal("10000000"); // 천만
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

값을 이용하여 변수명을 짓는 것은 좋지 않습니다.

BigDecimal overFiveM = new BigDecimal("5000000"); // 오백만
BigDecimal overM = new BigDecimal("1000000"); // 백만
BigDecimal overTenT = new BigDecimal("10000"); // 만

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수 안에서 객체를 정의하여 할당하고 있습니다. 이렇게 되면 함수를 호출할 때마다 객체가 다시 정의되어 비효율적입니다. 함수 내부가 아닌 클래스 내부에 정의하는 것이 좋아보입니다.


if(amount.compareTo(overTenM) >= 0) { return new BigDecimal("0.5");} // 이자율 50%

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미리 필요한 BigDecimal 상수를 정적 변수로 선언하고 재사용하는 것이 좋습니다. 따라서 이자율은 상수로 관리하는 것이 좋아보입니다.

else if(amount.compareTo(overFiveM) >= 0) { return new BigDecimal("0.07");} // 이자율 7%
else if(amount.compareTo(overM) >= 0) { return new BigDecimal("0.04");} // 이자율 4%
else if(amount.compareTo(overTenT) >= 0) { return new BigDecimal("0.02");} // 이자율 2%

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else if는 코드 컨벤션상 사용하지 않는 것이 좋습니다.

else { return new BigDecimal("0.01");} // 이자율 1%
}
}
128 changes: 128 additions & 0 deletions src/CentralBank.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import java.math.BigDecimal;
import java.util.*;

public class CentralBank
{
private List<Account> accounts;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private List<Account> accounts;
private final List<Account> accounts = new ArrayList<>();

필드에 선언하는 것이 좋을 것 같습니다.
final키워드를 선언함으로써 변경되지 않을 것을 명시해줘야합니다

private Map<String, InterestCalculator> Calcualateinterest;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수명은 카멜케이스 표기법을 사용합시다!

Suggested change
private Map<String, InterestCalculator> Calcualateinterest;
private Map<String, InterestCalculator> calcualateInterest;

public CentralBank()
{
accounts = new ArrayList<>();
Calcualateinterest = new HashMap<>();
Calcualateinterest.put("N", new AccountInterestRate());
Calcualateinterest.put("S", new SavingAccountInterestRate());
}
// 게좌 만들기
public void createAccount(Account a)
{
if(a instanceof SavingAccount)
{
if(!((SavingAccount) a).compareCAGA(a))
{
accounts.add(a);
}
else
{
System.out.println("Your Gaol Amount is smaller than your Amount");
}
}
else
{
accounts.add(a);
}
}
// 전체 게좌 정보 출력
public void printAllAccountsInfo()
{
for (Account account : accounts)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for문은 java stream을 통해 최적화할 수 있습니다. 자바 스트림에 대하여 공부하여 적용해봅시다.
https://velog.io/@yun8565/Java-스트림Stream-정리

{
System.out.println(account.getAccountInfo());
}
}
// 출금
public void withdrawal(String accountNum, BigDecimal amount)
{
Account account = findAccountByNum(accountNum);
if (account != null)
{
account.withdrawal(amount);
}
else {System.out.println("Account not found!");}
}
// 입금
public void deposit(String accountNum, BigDecimal amount)
{
Account account = findAccountByNum(accountNum);
if (account != null)
{
account.deposit(amount);
}
else
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else문을 사용하지 않는 방식을 고려해봅시다.
early return 방식을 통해 else문을 제거할 수 있습니다.

System.out.println("Unavailable Account");
}
}

// 송금
public void transfer(String senderAccountNum, String receiverAccountNum, BigDecimal amount) {

Account sender = findAccountByNum(senderAccountNum);
Account receiver = findAccountByNum(receiverAccountNum);
if (sender != null && receiver != null)
{
sender.transfer(receiver, amount);
} else
{
System.out.println("Sender or Receiver account not found!");
}
}

// 계좌번호로 계좌 찾기
private Account findAccountByNum(String accountNum)
{
for (Account account : accounts)
{
if (account.getAccountNum().equals(accountNum))
{
return account;
}
}
return null;
}

// 이자를 계산하고 출력

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이자를 계산하고 / 출력... 두가지 역할을 하나의 함수에서 하고있습니다. 하나의 함수는 하나의 역할만 하도록 함수를 분리하여 단일책임원칙을 지키도록 코드를 작성해주세요

public void printAccountInfoNInterestRate(String accountNum)
{
for (Account account : accounts)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

java stream 사용해봅시다!

{
if(account.getAccountNum().equals(accountNum))
{
InterestCalculator calculator = Calcualateinterest.get(account.getAccountType());
BigDecimal interest = calculator.getInterest(account.getAmount());
System.out.println("Account Owner : " + account.getOwner() + ", Account Number: " + account.getAccountNum() + ", Interest : " + interest);
}
}
}

public void addInterest()
{
if(accounts != null)
{
for (Account account : accounts)
{
InterestCalculator calculator = Calcualateinterest.get(account.getAccountType());
BigDecimal interestRate = calculator.getInterest(account.getAmount());
BigDecimal interestAmount = account.getAmount().multiply(interestRate);

account.setAmount(account.getAmount().add(interestAmount));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

account의 amount를 추가하는 로직은 Account클래스의 책임이지 않을까요!?

System.out.println("Finished adding Interest " + interestAmount + " to " + account.getOwner());
}
System.out.println("Finished adding Interest!");
}
else
{
System.out.println("Accounts are not exist!");
}
}

}
7 changes: 7 additions & 0 deletions src/InterestCalculator.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인터페이스를 사용하는 이유는 무엇일까요?
현재는 인터페이스의 장점을 사용했다고 보기 어렵고 이 인터페이스의 존재 이유도 잘 모르겠습니다.
https://gofnrk.tistory.com/22

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import java.math.BigDecimal;

public interface InterestCalculator
{
BigDecimal getInterest(BigDecimal amount);
}

161 changes: 161 additions & 0 deletions src/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import java.math.BigDecimal;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main Class는 전체적인 어플리케이션을 실행시키는 역할인데, 어떤 어플리케이션인지 고민하면서 클래스 이름도 변경해보는 것도 좋을 것 같습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main 클래스가 너무 많은 책임을 지고 있지 않나 고민해보셔야 할 것 같습니다.
현재 Main클래스는 흐름제어, 입력, 출력, Account, validate 등 관련된 여러 가지 책임을 맡고 있는 것 같습니다.
흐름제어만을 남기고 따로 객체를 분리해주세요.

{
Pattern pattern = Pattern.compile("\\d{6}-\\d{2}-\\d{6}");
public int showMenu(Scanner s)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

화면을 출력해주는 책임을 가진 객체를 따로 분리하시는 것이 좋을 것 같습니다

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public int showMenu(Scanner s)
private int showMenu(Scanner s)

외부에 드러내도 되는 메소드인가, 외부에서 사용될 메소드인가를 고민하셔서 접근제어자를 설정해야합니다.

{
System.out.println("*********메뉴********");
System.out.println("1. 계좌 생성");
System.out.println("2. 인출");
System.out.println("3. 입금");
System.out.println("4. 송금");
System.out.println("5. 이자보기");
System.out.println("6. 전체 계좌 보기");
System.out.println("7. 각 계좌에 이자 더하기");
System.out.println("8. 나가기");
System.out.print("원하시는 작업을 선택해주세요 : ");
return s.nextInt();
}
public Account getAccount(Scanner s)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAccount의 메소드가 단지 account를 얻는 책임만을 가지고 있는지 고민해봐야 할 것 같습니다.
이름 생성, 입력, 출력, 계좌 종류 체크, 인스턴스 생성 의 책임을 가지고 있습니다.
너무 많은 책임을 지고 있는 것은 유지보수에 좋지 않습니다. 책임을 분리해야합니다

{
String name;
String type;
String aNum;
BigDecimal amount;
BigDecimal goalAmount;

System.out.print("성명을 입력해주세요 : ");
name = s.next();

type = getAccountType(s);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type은 어떤 것을 의미하나요? 변수명을 좀더 구체적으로 지어주세요

aNum = getAccountNum(s);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aNum은 accountNumber를 의미하는 것인가요? 줄여쓰는 것은 좋지 않습니다

amount = getAmount(s);

if(type.equals("S"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S가 의미하는 바는 무엇인가요? 어떤 다른 사람이 봐도 이해할 수 있게 구체적으로 작성해야 합니다.
따로 변수를 선언해주세요

{
goalAmount = getGoalAmount(s, amount);
return new SavingAccount(type, aNum, name, amount, goalAmount);
}
else {return new Account(type, aNum, name, amount);}

}

public String getAccountType(Scanner s)
{
System.out.println("예금계좌 : N, 적금게좌 : S");
System.out.print("계좌 타입을 입력해주세요 : ");
String at = s.next();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수명을 구체적으로 작성해주세요

while(!at.equals("N") && !at.equals("S"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예외를 발생시켜 처리하는 것이 좋습니다

{
System.out.println("잘못된 입력값입니다.");
System.out.print("계좌타입을 다시 입력해주세요 : ");
at = s.next();
}
return at;
}

public boolean isNumber(String s)
{
try {Double.parseDouble(s);}
catch (NumberFormatException e)
{
return false;
}
return true;
}

public String getAccountNum(Scanner s)
{
System.out.println("계좌번호를 입력하세요");
String an = s.next();
Matcher m = pattern.matcher(an);
while(!m.matches())
{
System.out.println("잘못된 입력값입니다.");
System.out.print("계좌번호를 다시 입력해주세요 : ");
an = s.next();
m = pattern.matcher(an);
}
return an;
}
public BigDecimal getAmount(Scanner s)
{
System.out.println("금액을 입력하세요");
String am = s.next();
while(!isNumber(am))
{
System.out.println("잘못된 입력값입니다.");
System.out.print("예산을 다시 입력해주세요 : ");
am = s.next();
}
return new BigDecimal(am);
}
public BigDecimal getGoalAmount(Scanner s, BigDecimal current)
{
System.out.println("적금 목표 금액을 입력하세요");
String gm = s.next();
while(!isNumber(gm))
{
System.out.println("잘못된 입력값입니다.");
System.out.print("목표 금액을 다시 입력해주세요 : ");
gm = s.next();
}
return new BigDecimal(gm);
}

public String getReceiverAccountNum(Scanner s)
{
System.out.println("송금하실 계좌번호를 입력하세요");
String an = s.next();
Matcher m = pattern.matcher(an);
while(!m.matches())
{
System.out.println("잘못된 입력값입니다.");
System.out.print("계좌번호를 다시 입력해주세요 : ");
an = s.next();
m = pattern.matcher(an);
}
return an;
}
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
Main m = new Main();
CentralBank c = new CentralBank();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의미있는 변수명을 사용해주세요


while(true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반복문 조건에 true를 명시하는 것은 위험합니다. 다른 방법을 생각해보세요

{
int i = m.showMenu(s);
Copy link

@Erichong7 Erichong7 Apr 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 selectedNumber와 같이 의미있는 변수명을 가졌으면 좋겠습니다.
코딩의 꽃은 협업입니다.

switch(i)
{
case 1:
c.createAccount(m.getAccount(s));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAccount()가 Main의 책임인지 고민해봐야합니다

break;
case 2:
c.withdrawal(m.getAccountNum(s), m.getAmount(s));
break;
case 3:
c.deposit(m.getAccountNum(s), m.getAmount(s));
break;
case 4:
c.transfer(m.getAccountNum(s), m.getReceiverAccountNum(s), m.getAmount(s));
break;
case 5:
c.printAccountInfoNInterestRate(m.getAccountNum(s));
break;
case 6:
c.printAllAccountsInfo();
break;
case 7:
c.addInterest();
break;
case 8:
return;
}
}
}
}
Loading