[싱글톤 패턴 ]
어플리케이션에서 단 한개의 객체만 생성해서 사용하고 싶음
[특징]
생성자를 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; 는 부적절함
싱글톤을 사용하는 이유
개념 : 먼저 회사에 프린터기가 있다고 생각해보자 . 그럼 회사 직원들은 그 하나의 프린터기만 사용해야한다.
이런 개념은 프로그래밍에서도 적용되는데, 하나의 객체를 여러 객체들이 공유하며 사용해야할 때 싱글톤 패턴을 사용한다.
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
'Development (국비 복습 ) > 자바' 카테고리의 다른 글
[2023-03-30]예외처리,제네릭, (0) | 2023.03.30 |
---|---|
[2023-03-23] static,접근제한자(private),getter,setter (0) | 2023.03.23 |
[2023-03-22]인스턴스,VO,오버로딩 (0) | 2023.03.22 |
[2023-03-21]배열 중복값, (0) | 2023.03.21 |
[2023-03-20]배열복사,다차원 배열,자바 문제 (0) | 2023.03.20 |
댓글