본문 바로가기
Development (국비 복습 )/자바

[2023-03-24]싱글톤,은행계좌만들기

by Puddingforever 2023. 3. 24.

[싱글톤 패턴 ]

어플리케이션에서 단 한개의 객체만 생성해서 사용하고 싶음 

[특징]

생성자를 private으로 만든다 (즉 , 생성자를 밖에서 못씀, 클래스 내부에서만 생성자가 사용됨) 

new 생성자() 코드로 밖에서 객체를 생성할 수 없음

myBatis나 Spring 이 이런 형태로 되어있다. 

 

 

[과정]

1.필드가 private인 객체를 만들어준다. 

public class Person {
	//필드 private : 값을 마음대로 수정하고 , 읽어올 수 없음 
	private String myName;
	private int myAge;
	
	//getter 
	public String getMyName() {
		return this.myName;
	}
	public int getMyAge() {
		return this.myAge;
	}
	
	//setter
	public void setMyName(String myName) {
		this.myName=myName;
	}
	public void setMyAge(int myAge) {
		this.myAge = myAge;
	}
	
}

2.싱글톤 패턴을 사용하기 위해 생성자를 private으로 만들어준다. (밖에서 객체를 못만든다) 

public class Person {
	//field
	private String myName;
	private int myAge;
	
	//getter 
	public String getMyName() {
		return this.myName;
	}
	public int getMyAge() {
		return this.myAge;
	}
	
	//setter
	public void setMyName(String myName) {
		this.myName=myName;
	}
	public void setMyAge(int myAge) {
		this.myAge = myAge;
	}
	
	//생성자 추가 , 다른 틀의 객체를 만들 수 없다. 
	private Person() {
	}
    //파라메터 생성자는 사실 필요없음 
	private Person(String myName,int myAge) {
		this.myName = myName;
		this.myAge = myAge;
	}
}

 

 

하지만 싱글톤 패턴은 밖에서 객체를 못만들기 때문에 파라메터 생성자는 사실 필요없다. 

 

3.이제 Person 객체 안에서 이 객체가 단 한개만 쓸 수 있다는 것을 설정해준다. 

public class Person {
	//field
	private String myName;
	private int myAge;
	
	//getter 
	public String getMyName() {
		return this.myName;
	}
	public int getMyAge() {
		return this.myAge;
	}
	
	//setter
	public void setMyName(String myName) {
		this.myName=myName;
	}
	public void setMyAge(int myAge) {
		this.myAge = myAge;
	}
	
	//생성자 추가 
	private Person() {
	}
   
    //객체안에 객체를 넣은 변수를 만듬 , 단 한개만 존재해야하는 객체,  myPerson! 
    private static Person myPerson = new Person();
}

 

4.단 한개의 객체만 불러내는 메소드를 만듬

public class Person {
	//field
	private String myName;
	private int myAge;
	
	//getter 
	public String getMyName() {
		return this.myName;
	}
	public int getMyAge() {
		return this.myAge;
	}
	
	//setter
	public void setMyName(String myName) {
		this.myName=myName;
	}
	public void setMyAge(int myAge) {
		this.myAge = myAge;
	}
	
	//생성자 추가 
	private Person() {
	}
 
	//Person객체를 가지고 있는 필드 
	private static Person myPerson = new Person();
    
    //객체를 불러내는 메소드를 만들어줌 
    
    public static Person getPersonInstance(){
    	return myPerson;
    }
}

즉 myPerson이라는 인스턴스는 getPersonInstance()가 실행될 때만 불러진다. 

Person.getPersonInstance() 를 하면 , Person이라는 객체가 불러지는 것이다. 

 

 

5.메인메소드에서 객체를 불러오기 

public static void main(String[] args) {
		
        Person myPerson = Person.getPersonInstance();
        Person yourPerson = Person.getPersonInstance();
		
		if(myPerson == yourPerson) { 
			System.out.println("두 변수는 동일한 하나의 객체를 사용합니다.");
		}else{
			System.out.println("두 변수는 서로 다른 객체를 사용합니다.");
		}

만약 메인메소드에서 Person myPerson = new Person() 이런형태로 쓰면 에러가 난다. 생성자가 private으로 밖에서 못쓰게 막아놨기 때문이다. 

getPersonInstance()는 , 자기 자신을 반환한다. 즉 , 객체 자신을 리턴하기 때문에 , 둘이 같은 객체의 메모리 주소를 쓰기 때문에 == 비교로 true 가 나온다. 

 

싱글톤 패턴의 주의사항

 

Person myPerson = Person.getPersonInstance();
Person yourPerson = Person.getPersonInstance();

//사용시 주의 사항
myPerson.setMyName("홍길동");
myPerson.setMyAge(30);

System.out.println("myPerson 이름: "+myPerson.getMyName());
System.out.println("myPerson 이름: "+myPerson.getMyAge());

System.out.println("yourPerson 이름: "+yourPerson.getMyName());
System.out.println("yourPerson 이름: "+yourPerson.getMyAge());

둘다 홍길동, 30나옴

 

따라서 객체 안에 객체 고유의 특징을 가지는 값을 만들어놓으면 안됌 !!!! 

서로서로 공유해서 써야하기 때문이다.

따라서 private String myName; or private int myAge; 는 부적절함 


싱글톤을 사용하는 이유

 

개념 : 먼저 회사에 프린터기가 있다고 생각해보자 . 그럼 회사 직원들은 그 하나의 프린터기만 사용해야한다. 

이런 개념은 프로그래밍에서도 적용되는데, 하나의 객체를 여러 객체들이 공유하며 사용해야할 때  싱글톤 패턴을 사용한다.

 

 

https://velog.io/@neity16/Spring-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8-5-%EC%8B%B1%EA%B8%80%ED%86%A4Singleton%EA%B3%BC-%EC%8A%A4%ED%94%84%EB%A7%81

 

Spring 핵심 원리 기본편 (5) - 싱글톤(Singleton)과 스프링(Spring)

싱글톤(Singelton) ? > : 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴 > > 스프링은 태생이 기업용 온라인서비스 기술을 지원하기 위해 탄생 --> 대부분 웹 애플리케이션이며,

velog.io

달력같은 경우 여러객체가 하나의 달력을 값을 같이 봐야하기 때문에 싱글톤으로 구성되어있다. 

아니면 @Autowired의 경우도 싱글톤을 사용한 케이스이다(객체를 안생성해도 객체를 불러올 수 있음) ! 

 


은행 계좌 만들기 

 

Account 계좌 

public class Account {
	private String accNo;
	private String accName;
	private int balance;
	
	//상수 , 잔액은 0이상 1000000 이하
	
	private static final int MIN_BALANCE = 0;
	private static final int MAX_BALANCE = 1000000;
	
	public Account(String accNo,String accName,int balance) {
		this.accNo = accNo;
		this.accName = accName;
		this.balance = balance;
	}
	
	public String getAccNo() {
		return this.accNo;
	}
	
	public void setAccNo(String accNo) {
		this.accNo = accNo;
	}
	
	public String getAccName() {
		return this.accName;
	}
	
	public void setAccName(String accName) {
		this.accName = accName;
	}
	
	public int getBalance() {
		return this.balance; 
	}
	
	public void setBalance(int balance) {
		if(balance>MIN_BALANCE&&balance<=MAX_BALANCE) {
			this.balance = balance;
		}else {
			this.balance = 1000;
		}
		
	}

}

 

실행메소드 

 

package test_question;

import java.util.Scanner;
import java.util.regex.Pattern;

import javax.swing.plaf.synth.SynthOptionPaneUI;

public class BankApplication {
	
	//필드 
	public static Account[] accountArray = new Account[100] ;
	public static Scanner myScanner = new Scanner(System.in) ;
	public static String accountNumber;
	public static String balanceStr;
	public static long balance;
	
	//사용자가 입력한 계좌번호를 가진 Account 타입 객체를 찾아서 배열에서의 위치 인덱스를 반환하는 메소드를 
	//먼저 구현 
	public static int findAccountIdx(String accountNumber) {
		int idx = -1 ;
		for(int i=0;i<accountArray.length;i++) {
			if(accountArray[i]!=null&&accountArray[i].getAccountNumber().equals(accountNumber)) {
				idx = i;
				break;//중복되면 안되서 break함 ! 
			}//else 문을 적을 필요는 없다. 못찾았으면 그냥 뭔가 하는 일은 없고 걍 , 계속 반복문만 돌게 해준다. 
		}//end-for
		return idx;
	}
	
	//금액 입금 형식 검사 -> 입력값은 항상 문자형으로 받는다. 키보드 키가 많기 때문에 여러 가능성을 열어둔다.
	public static boolean isNumberStr(String numberStr) {
		return Pattern.matches("\\d+", numberStr); //양수만 되는 것이다. -로 안되어있어서 
	}
	
	//Account 객체를 Account[]의 빈 공간에 대입하는 메소드 
	public static boolean registerAccountArray(Account custAccount) {
		int idx = -1;
		for (int i = 0 ; i < accountArray.length ; i++ ) {
			if (accountArray[i] == null) {
				accountArray[i] = custAccount ;
				idx = i; 
				break ;
			}
			//idx 가 안나오면 , i로 셋팅이 안됌 
		}
		if(idx >= 0) {
			//값이 잘 대입되면 인덱스 값이 나오고 , 
			return true;
		}
			
		return false;
	}
	
	//계좌 등록 메소드
	public static void createAccount() {

		System.out.println("-------");
		System.out.println("계좌 생성");
		System.out.println("-------");
		
		System.out.print("계좌번호> ");
		accountNumber = myScanner.next() ;
		
		System.out.print("계좌주> ");
		String owner = myScanner.next() ;
		while (true) {
			System.out.print("초기입금액> ");
			balanceStr = myScanner.next() ;
			
			if (isNumberStr(balanceStr)) {
				balance = Long.parseLong(balanceStr) ;
				break ;
			} else {
				System.out.println("숫자키로 금액을 입력하십시오.");
				continue ;
			}	
		}
		
		Account custAccount = new Account(accountNumber, owner, balance) ;
		
		
		if(registerAccountArray(custAccount)) {
			
			System.out.println("결과: 계좌가 생성되었습니다.");

		}else {
			System.out.println("결과: 계좌 등록이 실패했습니다.");
		}
		
	}
	

	   
	   
	   //계좌 목록 메소드 
	   public static void createAccountList() {
	       
		   System.out.println("-------");
	       System.out.println("계좌 생성");
	       System.out.println("-------");
	       
	       for (Account account : accountArray) {
	          if (account != null) {
	             System.out.println( account.getAccountNumber() +  "\t" 
	                               + account.getOwner() + "\t"
	                               + account.getBalance()  );   
	          }
	       }
	   }

	   //예금
	   public static void deposit() {
		   
		   //예금

	       System.out.println("-------");
	       System.out.println("예금");
	       System.out.println("-------");
	       
	       System.out.print("계좌번호> ");
	       accountNumber = myScanner.next();
	       //계좌검색
	     int accountIdx = findAccountIdx(accountNumber);
	     
	     if(accountIdx == -1) {
	    	 System.out.println("결과: 계좌를 확인 후 다시 거래해주세요");
	    	 return; //deposit 메소드를 끝내버림 
	     }
	     
	     //계좌를 찾은 경우 
	     while(true) {
	    	 System.out.print("예금액> ");
		     balanceStr = myScanner.next();
		     if(!isNumberStr(balanceStr)) {
		    	System.out.println("숫자키로 입력해주세요.");
		    	continue;
		     } else {
		    	 balance = Long.parseLong(balanceStr);
		    	 break;
		     }
	     }
	    
	     //long _balance = balance + accountArray[accountIdx].getBalance();
	    //accountArray[accountIdx].setBalance(_balance);
	     
	     accountArray[accountIdx].setBalance(balance + accountArray[accountIdx].getBalance());
	     
	     System.out.println("결과: 예금이 성공되었습니다.");
	     
	       
	   }
	   
	   //출금
	   public static void withdraw() {
		   System.out.println("-------");
			System.out.println("출금");
			System.out.println("-------");
			System.out.print("계좌번호> ");
			
			accountNumber = myScanner.next();
			//계좌검색
		     int accountIdx = findAccountIdx(accountNumber);
		     //계좌번호가 없는 경우 
		     if(accountIdx == -1) {
		    	 System.out.println("결과: 계좌를 확인 후 다시 거래해주세요");
		    	 return; //deposit 메소드를 끝내버림 
		     }
		     //계좌를 찾은 경우 
		     while(true) {
		    	 System.out.print("출금액> ");
			     balanceStr = myScanner.next();
			     if(!isNumberStr(balanceStr)) {
			    	System.out.println("숫자키로 입력해주세요.");
			    	continue;
			     } else {
			    	 balance = Long.parseLong(balanceStr);
			    	 break;
			     }
		     }
			
			//입력된 balance를 입력한 계좌번호를 가진 Account 객체를
			//Account[] 에서 찾아서, Account 객체의 기존 balance += balance ;
		
		     long _balance=accountArray[accountIdx].getBalance();
		     
		     if(_balance < balance) {
		    	 System.out.println("잔액은 " + _balance+"입니다.");
		    	 System.out.println("잔액이 부족합니다.");
		    	 return ;
		     }
		     
		     accountArray[accountIdx].setBalance(_balance-balance);
		     System.out.println("결과: 출금이 성공되었습니다.");
			
	   }
	   
	   public static void removeAccount() {
		   
		   
		   System.out.println("-------");
	       System.out.println("삭제");
	       System.out.println("-------");
	       
	       System.out.print("계좌번호> ");
	       accountNumber = myScanner.next();
	       
	       int accountIdx = findAccountIdx(accountNumber);
	       
	       //계좌가 없는 경우 
	       if(accountIdx == -1) {
	    	   System.out.println("다시 거래하셈");
	    	   return;
	       }
	       
	       long _balance = accountArray[accountIdx].getBalance();
	       
	       if(_balance == 0) { 
	    	   	accountArray[accountIdx] = null;
    	   		System.out.println("계좌삭제 완료");
    	   		return;
	       }
	    
	       if(_balance>0) {
	    	  System.out.println("잔액이 "+_balance+"원 남아있습니다.");
	    	  System.out.println("잔액을 찾으신 후 , 계좌 삭제를 계속하시겠습니까?(Y/N) ");
	    	  if(myScanner.next().equalsIgnoreCase("N")) {
	    		  System.out.println("계좌삭제가 취소되었습니다.");
	    		  return ;
	    	  }
	    	  withdraw();	 
	    	  _balance = accountArray[accountIdx].getBalance();
	    	  if(_balance==0) {
	    		 accountArray[accountIdx] = null;
	    		 System.out.println("계좌삭제 완료");
	    	  }else {
	    		  System.out.println("잔액이 "+_balance+"원 남아있어서,");
	    		  System.out.println("결과 : 계좌 삭제가 취소되었습니다.");
	    	  }
	       }
	       
	       accountArray[accountIdx] = null;
	       
	      
	       
	       System.out.println("결과:"+ accountNumber +"삭제가 성공되었습니다.");
	       
	   }
	   
	   
public static void main(String[] args) {
		boolean runFlag = true ;
		while (runFlag) {
			System.out.println("-------------------------------------------------------");
			System.out.println("1.계좌생성 | 2.계좌목록 | 3.예금 | 4.출금 | 5.종료 | 6.게정삭제 ");
			System.out.println("-------------------------------------------------------");
			System.out.print("선택> ");
			
			String selectedOption = myScanner.next();
			
			if (selectedOption.equals("1")) {
				createAccount();
			} else if (selectedOption.equals("2")) {
				createAccountList();
			} else if (selectedOption.equals("3")) {
				deposit();
			} else if (selectedOption.equals("4")) {
				withdraw();
			} else if (selectedOption.equals("5")) {
				return ;	
			} else if(selectedOption.equals("6")) {
				removeAccount();
			} else {
				System.out.println("원하는 작업에 해당하는 1~6 사이의 숫자를 하나 입력하세요.");
				continue ;
			}
			
			
		}//while-end
		
		
	} //main-end
	

} //class-end

댓글