일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바의 정석 기초편 ch11
- jpa 활용2 - api 개발 고급
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch2
- 게시글 목록 api
- 스프링 mvc2 - 로그인 처리
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch1
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch7
- 자바의 정석 기초편 ch5
- 스프링 db1 - 스프링과 문제 해결
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch8
- 자바의 정석 기초편 ch6
- 자바의 정석 기초편 ch14
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch3
- 코드로 시작하는 자바 첫걸음
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch4
- 타임리프 - 기본기능
- 스프링 입문(무료)
- 스프링 mvc2 - 검증
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch9
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch13
- @Aspect
- 스프링 mvc1 - 서블릿
- Today
- Total
나구리의 개발공부기록
접근제어자, 접근 제어자 이해, 접근 제어자 종류, 접근 제어자 사용(필드,메서드,클래스레벨) 캡슐화, 문제와 풀이 본문
접근제어자, 접근 제어자 이해, 접근 제어자 종류, 접근 제어자 사용(필드,메서드,클래스레벨) 캡슐화, 문제와 풀이
소소한나구리 2025. 1. 4. 16:27출처 : 인프런 - 김영한의 실전 자바 - 기본편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 접근 제어자 이해
1) 접근 제어자가 필요한 이유
- 자바는 public, private 같은 접근 제어자(access modifier)를 제공함
- 접근 제어자를 사용하면 해당 클래스 외부에서 특정 필드나 메서드에 접근하는 것을 허용하거나 제한할 수 있음
(1) 예제 요구사항 - 스피커에 들어가는 소프트웨어 개발
- 스피커는 음량을 높이고, 내리고, 현재 음량을 확인할 수 있는 단순한 기능을 제공
- 요구사항 대로 스피커의 음량을 100까지만 증가할 수 있고 절대 100을 넘어가면 안됨(100을 넘어가면 스피커의 부품들이 고장난다고 가정)
(2) Speaker
- 생성자를 통해 초기 음량값을 지정할 수 있음
- volumeUp()메서드에서 음량을 한번에 10씩 증가시키고 음량이 100을 넘게되면 음량을 증가하지 않음
package access;
public class Speaker {
int volume;
Speaker(int volume) {
this.volume = volume;
}
void volumeUp() {
if (volume >= 100) {
System.out.println("음량을 증가할 수 없습니다. 최대 음량입니다");
} else {
volume += 10;
System.out.println("음량을 10 증가합니다");
}
}
void volumeDown() {
volume -= 10;
System.out.println("volumeDown 호출");
}
void showVolume() {
System.out.println("현재 음량: " + volume);
}
}
(2) SpeakerMain
- 스피커를 실행하는 클래스
- 초기 음량 값을 90으로 지정하고 음량을 높이는 메서드를 여러번 호출하면 기대한 대로 음량을 100을 넘기지 않음
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
}
}
/* 실행 결과
현재 음량90
음량을 10 증가합니다
현재 음량100
음량을 증가할 수 없습니다. 최대 음량입니다
현재 음량100
*/
(3) SpeakerMain - 필드 직접 접근 코드 추가
- 오랜 시간이 흘러서 업그레이드 버전의 스피커가 출시하게 되고 새로운 개발자가 급하게 기존 코드를 이어 받아서 개발을 하게 되었고 새로운 개발자는 기존 요구사항을 잘 몰랐다고 가정
- 코드에서 음량이 100이상 올라가지 않은 부분을 소리를 더 올리면 좋겠다고 생각한 개발자가 코드를 분석한 끝에 Speaker클래스에서 volume 필드를 직접 사용할 수 있다는 것을 발견하고 volume 필드의 값을 200으로 직접 설정하고 코드를 실행한 순간 기존의 요구사항에 따라 스피커의 부품이 고장이 발생되었음
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
// 필드에 직접 접근
System.out.println("volume 필드 직접 접근 수정");
speaker.volume = 200;
speaker.showVolume();
}
}
/* 실행 결과
현재 음량: 90
음량을 10 증가합니다
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다
현재 음량: 100
volume 필드 직접 접근 수정
현재 음량: 200
*/
(4) 문제가 발생한 이유
- Speaker 객체를 사용하는 사용자는 Speaker의 volume 필드와 메서드에 모두 접근할 수 있었음
- volumeUp() 메서드에서 음량이 100을 넘지 못하도록 기능 개발을 잘 했지만 Speaker를 사용하는 입장에서 volume필드에 직접 접근해서 원하는 값을 설정할 수 있었기 때문에 아무 소용이 없었음
- 이런 문제를 근본적으로 해결하기 위해서 volume필드의 외부 접근을 막을 수 있는 방법이 필요한데 이것이 접근 제어자임
2) 접근 제어자로 문제 해결
(1) Speaker - volume 접근 제어자를 private로 수정
- private 접근 제어자는 모든 외부 호출을 막아서 private이 붙은 경우 해당 클래스 내부에서만 호출 할 수 있음
package access;
public class Speaker {
private int volume;
// ... 기존 코드 동일
}
(2) volume 필드 - private 변경 후
- volume필드를 private을 사용해서 Speaker 내부에 숨겼으므로 외부에서 volume 필드에 직접 접근 할 수 없게 됨
- 이 상태에서 SpeakerMain 코드를 실행해보면 아래처럼 컴파일 오류가 발생하며 실행하기 전에도 IDE에서 speaker.volume = 200; 부분에 오류가 발생하는 것을 확인할 수 있음
- volume 필드는 private으로 설정되어 있기 때문에 외부에서 접근할 수 없다는 오류임
/Users/jinagyeomi/Desktop/dev/study/java/2.java-basic/src/access/SpeakerMain.java:16:16
java: volume has private access in access.Speaker
(3) volume 필드 직접 접근 부분 주석처리
- 필드에 직접 접근하는 부분을 주석처리하면 정상적으로 애플리케이션이 실행됨
- 만약 Speaker 클래스를 개발하는 개발자가 처음부터 private을 사용해서 volume 필드의 외부 접근을 막아두었다면 새로운 개발자도 volume 필드에 직접 접근하지 않고 volumeUp()과 같은 메서드를 통해서 접근 했을 것이며 결과적으로 Speaker가 고장나는 문제가 발생하지 않았을 것임
** 참고
- 좋은 프로그램은 무한한 자유도가 주어지는 프로그램이 아니라 적절한 제약을 제공하는 프로그램임
- 무한한 자유도는 무한한 버그를 유발한다..
package access;
public class SpeakerMain {
public static void main(String[] args) {
// ... 기존 코드 동일
// 필드에 직접 접근
System.out.println("volume 필드 직접 접근 수정");
// speaker.volume = 200;
speaker.showVolume();
}
}
2. 접근 제어자 종류
1) 접근 제어자 종류
(1) 접근 제어자
- private: 모든 외부 호출을 막음
- default(package-private): 같은 패키지안에서 호출은 허용함(아무것도 적지 않음)
- protected: 같은 패키지안에서 호출은 허용하며 패키지가 달라도 상속 관계의 호출은 허용, 상속 관계에서 자세히 설명
- public: 모든 외부 호출을 허용
- 순서대로 private이 가장 많이 차단하고 public이 가장 많이 허용함
- private -> default -> protected -> public
(2) package-private
- 접근 제어자를 명시하지 않으면 같은 패키지 안에서 호출을 허용하는 default 접근 제어자가 적용됨
- default라는 용어는 해당 접근 제어자가 기본값으로 사용되기 때문에 붙여진 이름이지만 실제로는 package-private이 더 정확한 표현이며 해당 접근 제어자를 사용하는 멤버는 동일한 패키지 내의 다른 클래스에서만 접근이 가능하기 때문임
- 그러나 두 용어를 함께 사용함
(3) 접근 제어자 사용 위치
- 접근 제어자는 필드와 메서드, 생성자에 사용됨
- 추가로 클래스 레벨에도 일부 접근 제어자를 사용할 수 있음
(4) 사용 예시
public class Speaker { //클래스 레벨
private int volume; //필드
public Speaker(int volume) {} //생성자
public void volumeUp() {} //메서드
public void volumeDown() {}
public void showVolume() {}
}
(5) 접근 제어자의 핵심은 속성과 기능을 외부로부터 숨기는 것!
- private: 나의 클래스 안으로 속성과 기능을 숨길 때 사용, 외부 클래스에서 해당 기능을 호출 할 수 없음
- default: 나의 패키지 안으로 속성과 기능을 숨길 때 사용, 외부 패키지에서 해당 기능을 호출 할 수 없음
- protected: 상속 관계로 속성과 기능을 숨길 때 사용, 상속 관계가 아닌 곳에서 해당 기능을 호출할 수 없음
- public: 기능을 숨기지 않고 어디서든 호출할 수 있게 공개
3. 접근 제어자 사용
** 주의
- 지금 부터는 패키지의 위치가 매우 중요하므로 패키지 위치에 주의해야함
1) 필드, 메서드 레벨의 접근 제어자
(1) AccessData
- access하위에 a 패키지를 생성 후 작성, 패키지 위치를 꼭 맞추어야 함
- 순서대로 public, default, private을 필드와 메서드에 사용
- 마지막의 innerAccess()메서드는 내부 호출을 보여주는데, 내부 호출은 자기 자신에게 접근하는 것이므로 private을 포함한 모든 곳에 접근할 수 있음
package access.a;
public class AccessData {
public int publicField;
int defaultField;
private int privateField;
public void publicMethod() {
System.out.println("publicMethod 호출 " + publicField);
}
void defaultMethod() {
System.out.println("defaultMethod 호출 " + defaultField);
}
private void privateMethod() {
System.out.println("privateMethod 호출 " + privateField);
}
public void innerAccess() {
System.out.println("내부 호출");
publicField = 100;
defaultField = 200;
privateField = 300;
publicMethod();
defaultMethod();
privateMethod();
}
}
(2) AccessInnerMain - 외부에서 위에서 만든 클래스에 접근
- 위와 동일한 access.a 패키지에 작성됨
- public은 모든 접근을 허용하기 때문에 필드, 메서드 모두 접근 가능함
- default는 같은 패키지에서 접근할 수 있는데 AccessInnerMain과 AccessData는 같은 패키지에 있으므로 default 접근 제어자에 접근할 수 있음
- private은 AccessData 내부에서만 접근할 수 있으므로 호출 불가하여 주석 처리해야 프로그램이 실행 됨
- AccessData.innerAccess() 메서드는 public이므로 외부에서 호출할 수 있음
- innerAccess()메서드는 외부에서 호출 되었지만 innerAccess()메서드는 AccessData에 포함되어 있기때문에 자신의 private 필드와 메서드에 모두 접근할 수 있음
package access.a;
public class AccessInnerMain {
public static void main(String[] args) {
AccessData data = new AccessData();
// public 호출 가능
data.publicField = 1;
data.publicMethod();
// 같은 패키지 default 호출 가능
data.defaultField = 2;
data.defaultMethod();
// private 호출 불가
// data.privateField = 3;
// data.privateMethod();
data.innerAccess();
}
}
/* 실행 결과
publicMethod 호출 1
defaultMethod 호출 2
내부 호출
publicMethod 호출 100
defaultMethod 호출 200
privateMethod 호출 300
*/
(3) AccessOuterMain
- access 패키지하위에 b 패키지를 만들어서 작성
- public은 마찬가지로 모든 접근을 허용하기 때문에 문제없이 모두 접근할 수 있지만 위와 다르게 AccessOuterMain은 AccessData와 패키지의 위치가 다르기 때문에 default 접근 제어자에 접근할 수 없으므로 default 필드와 메서드 모두 오류가 발생함
package access.b;
import access.a.AccessData;
public class AccessOuterMain {
public static void main(String[] args) {
AccessData data = new AccessData();
// public 호출 가능
data.publicField = 1;
data.publicMethod();
// 같은 패키지 default 호출 가능
// data.defaultField = 2;
// data.defaultMethod();
// private 호출 불가
// data.privateField = 3;
// data.privateMethod();
data.innerAccess();
}
}
/* 실행 결과
publicMethod 호출 1
내부 호출
publicMethod 호출 100
defaultMethod 호출 200
privateMethod 호출 300
*/
** 참고
- 생성자도 접근 제어자 관점에서 메서드와 동일하게 적용됨
2) 클래스레벨
(1) 클래스 레벨의 접근 제어자 규칙
- 클래스 레벨의 접근 제어자는 public, default만 사용할 수 있고 private, protected는 사용할 수 없음
- public 클래스는 반드시 파일명과 이름이 같아야 함
- 하나의 자바 파일에 public 클래스는 하나만 등장할 수 있음
- 하나의 자바 파일에 default 접근 제어자를 사용하는 클래스는 무한정 만들 수 있음
(2) PublicClass.java 파일
- access.a 패키지에 생성
- PublicClass라는 이름의 public 클래스를 생성하면 이 클래스는 public 접근 제어자이므로 파일명과 이 클래스의 이름이 반드시 같아야하고 public이기 때문에 외부에서 접근할 수 있음
- DefaultClass2, DefaultClass2는 default 접근 제어자이므로 같은 패키지 내부에서만 접근할 수 있음
- main() 메서드의 내부를 보면 각각의 클래스를 사용하는 예시를 확인할 수 있음
package access.a;
public class PublicClass {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
DefaultClass1 class1 = new DefaultClass1();
DefaultClass2 class2 = new DefaultClass2();
}
}
class DefaultClass1 {
}
class DefaultClass2 {
}
(3) PublicClassInnerMain
- access.a 패키지에 생성
- PublicClass는 public 클래스이므로 당연히 접근이 가능하고 PublicClassInnerMain과 DefaultClass1, DefaultClass2는 같은 패키지이므로 아무 문제없이 접근할 수 있음
package access.a;
public class PublicClassInnerMain {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
DefaultClass1 class1 = new DefaultClass1();
DefaultClass2 class2 = new DefaultClass2();
}
}
(4) PublicClassOuterMain
- access.b 패키지에 생성
- PublicClass는 public 클래스이므로 외부 패키지에서도 접근이 가능함
- 그러나 PublicClassOuterMain과 DefaultClass1, DefaultClass2는 다른 패키지이므로 접근할 수 없어서 컴파일 오류가 발생함
- 당연히 import에서도 오류가 발생함
package access.b;
//import access.a.DefaultClass1;
//import access.a.DefaultClass2;
import access.a.PublicClass;
public class PublicClassOuterMain {
public static void main(String[] args) {
PublicClass publicClass = new PublicClass();
// 다른 패키지에서 접근 불가
// DefaultClass1 class1 = new DefaultClass1();
// DefaultClass2 class2 = new DefaultClass2();
}
}
4. 캡슐화
1) 캡슐화(Encapsulation)
(1) 설명
- 캡슐화는 객체 지향 프로그램의 중요한 개념 중 하나인데 데이터와 해당 데이터를 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것을 말하며 캡슐화를 통해서 데이터의 직접적인 변경을 방지하거나 제한할 수 있음
- 쉽게 이야기해서 속성과 기능을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것임
- 객체 지향 프로그래밍을 설명하면서 캡슐화에 대해 간단히 알아보았는데, 이때는 데이터와 데이터를 처리하는 메서드를 하나로 모으는 것에 초점을 맞추었다면 한발 더 나아가서 캡슐화를 안전하게 완성할 수 있게 해주는 장치가 바로 접근 제어자임
(2) 데이터를 숨겨라
- 객체에는 속성(Data)와 기능(메서드)가 있는데 캡슐화에서 가장 필수로 숨겨야 하는 것은 속성임
- Speaker의 volume의 예제를 떠올려 보면 객체 내부의 데이터를 외부에서 함부로 접근하게 두었을 때 클래스 안에서 데이터를 다루는 모든 로직을 무시하고 데이터를 변경하게 되어 모든 안전장치들을 무시하여 데이터를 변경할 수 있게 되고 결국엔 캡슐화가 무용지물이 됨
- 일상 생활을 생각해보면 자동차를 운전할 때 자동차 부품을 다 열어서 그 안에 있는 속도계를 직접 조절하지 않고 단지 자동차가 제공하는 엑셀 기능을 사용해서 엑셀을 밟으면 나머지는 자동차가 알아서 하게 되는 것이고,
음악 플레이어를 사용할 때 그 내부에 들어있는 전원부나 볼륨 상태의 데이터를 직접 수정하는 것이 아니라 그냥 음악 플레이어의 켜고 끄는 전원버튼과 볼륨을 조절하는 버튼을 누를 뿐이며 해당 버튼들을 눌렀을 때 내부의 장치들을 움직여서 동작하는 것은 음악 플레이어가 하는 일임 - 즉, 우리는 제공되는 각 기능을 통해서 음악플레이어나 자동차를 사용하는 것이고 복잡하게 그 내부의 데이터까지 직접 사용하지 않는 것과 동일함
- 객체의 데이터는 객체가 제공하는 기능인 메서드를 통해서 접근 해야함
(3) 기능을 숨겨라
- 전체의 기능을 숨기면 당연히 할 수 있는것이 없으므로 여기서 말하고자 하는 부분은 객체의 기능 중에서 외부에서 사용하지 않고 내부에서만 사용하는 기능들이 존재하는데 이런 기능도 모두 감추는 것이 좋음
- 자동차를 운전하기 위해 자동차가 제공하는 복잡한 엔진 조절 기능, 배기 기능 등은 사용자가 직접 알 필요 없고 엑셀, 브레이크, 핸들 조작 등의 기능만 알면 됨
- 만약 사용자에게 이른 기능까지 모두 알려준다면 사용자가 자동차에 대해 너무 많은 것을 알아야 하고 복잡하기만 할 뿐이며 문제만 생길 뿐이므로 사용자 입장에서 꼭 필요한 기능만 외부에 노출하고 나머지 기능은 모두 내부로 숨기는 것이 좋음
(4) 정리
- 정리하면 데이터는 모두 숨기고 기능은 꼭 필요한 기능을 노출하는 것이 좋은 캡슐화 설계임
2) 좋은 캡슐화 예제
(1) BankAccount
- access 패키지에 생성
- private
- balance: 데이터 필드는 외부에 직접 노출하지 않지 않으며 BankAccount가 제공하는 메서드를 통해서만 접근할 수 있음
- isAmountValid(): 입력 금액을 검증하는 기능은 해당 클래스의 내부에서만 필요한 기능이므로 private을 사용 - public
- deposit(): 입금
- withdraw(): 출금
- getBalance(): 잔고 - BankAccount를 사용하는 입장에서는 public메서드 3가지만 알면되며 나머지 복잡한 내용은 모두 BankAccount내부에 숨어 있음
package access;
public class BankAccount {
private int balance;
// 인스턴스 멤버는 자동으로 기본값으로 초기화 되므로 사실 없어도 되는 코드임, 예제에서는 public 생성자를 보여주기 위해서 적용
public BankAccount() {
this.balance = 0;
}
// public 메서드
public void deposit(int amount) {
if (isAmountValid(amount)) {
balance += amount;
} else {
System.out.println("유효하지 않은 금액입니다");
}
}
// public 메서드
public void withdraw(int amount) {
if (isAmountValid(amount) && balance - amount >= 0) {
balance -= amount;
} else {
System.out.println("유효하지 않은 금액이거나 잔액이 부족합니다");
}
}
// public 메서드
public int getBalance() {
return balance;
}
// private 메서드
private boolean isAmountValid(int amount) {
// 금액이 0보다 커야함
return amount > 0;
}
}
(2) BankAccountMain
- BankAccount를 사용하는 클래스는 BankAccount의 public 메서드만 사용할 수 있음
package access;
public class BankAccountMain {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(10000);
account.withdraw(3000);
System.out.println("balance: " + account.getBalance());
}
}
/* 실행 결과
balance: 7000
*/
(3) 만약의 상황
- 만약 isAmountValid()를 외부에 노출하게 되면 BankAccount를 사용하는 개발자 입장에서는 사용할 수 있는 메서드가 하나 더 늘었기 때문에 '입금, 출금 메서드를 사용하기 전에 isAmountValid()를 호출해서 직접 검증해야 하나?' 라고 의문을 가지게 되는 상황이 발생할 수 있음
- 만약 balance 필드를 외부에 노출하게 된다면 BankAccount를 사용하는 개발자 입장에서는 이 필드를 직접 사용해도 된다고 생각할 수 있음
- 왜냐하면 외부에 공개하는 것은 그것을 외부에서 사용해도 된다는 뜻이기 때문임
- 결국엔 모든 검증과 캡슐화가 깨지고 잔고를 무한정 늘리고 출금하는 심각한 문제가 발생할 수 있음
- 접근 제어자와 캡슐화를 통해 데이터를 안전하게 보호하는 것은 물론이고 BankAccount를 사용하는 개발자 입장에서 해당 기능을 사용하는 복잡도도 낮출 수 있음
5. 문제와 풀이
1) 최대 카운터와 캡슐화
(1) 요구사항
- MaxCounter 클래스를 생성, 해당 클래스는 최대값을 지정하고 최대값 까지만 값이 증가하는 기능을 제공
- int count: 내부에서 사용하는 숫자, 초기값은 0
- int max: 최대값, 생성자를 통해 입력
- increment(): 숫자를 하나 증가
- getcount(): 지금까지 증가한 값을 반환 - 접근제어자를 사용하여 데이터를 캡슐화 진행
- 해당 클래스는 다른 패키지에서도 사용할 수 있어야 함
(2) 문제
package access.ex;
public class CounterMain {
public static void main(String[] args) {
MaxCounter counter = new MaxCounter(3);
counter.increment();
counter.increment();
counter.increment();
counter.increment();
int count = counter.getCount();
System.out.println(count);
}
}
실행 결과
최대값을 초과할 수 없습니다.
3
(3) 정답
package access.ex;
public class MaxCounter {
private int count;
private int max;
public MaxCounter(int max) {
this.max = max;
}
public void increment() {
if (count >= max) {
System.out.println("최대값을 초과할 수 없습니다");
} else {
count++;
}
}
public int getCount() {
return count;
}
}
실행 결과는 동일
MaxCounter 클래스에서 increment()메서드를 아래와 같이 return 문을 활용할 수 있는데, 이렇게 검증로직과 실행 로직을 분리하여 검증로직이 모두 통과 되었을 때 실행로직이 동작하도록 코드를 작성할 수 있음
public void increment2() {
// 검증 로직 (다수 작성 후, 검증이 안되면 메서드 종료)
if (count >= max) {
System.out.println("최대값을 초과할 수 없습니다");
return;
}
// 검증이 모두 통과되면 실행될 실행 로직 작성
count++;
}
2) 쇼핑 카트
(1) 요구사항
- ShoppingCartMain코드가 작동하도록 Item, ShoppingCart 클래스를 완성
- 접근 제어자를 사용하여 데이터를 캡슐하고 해당 클래스는 다른 패키지에서도 사용할 수 있어야 함
- 장바구니에는 상품을 최대 10개만 담을 수 있음
- 10개 초과 등록시 "장바구니가 가득 찼습니다." 출력
(2) 문제
package access.ex;
public class ShoppingCartMain {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("마늘", 2000, 2);
Item item2 = new Item("상추", 3000, 4);
cart.addItem(item1);
cart.addItem(item2);
cart.displayItems();
}
}
package access.ex;
public class Item {
private String name;
private int price;
private int quantity;
// TODO 나머지 코드를 완성
}
public class ShoppingCart {
private Item[] items = new Item[10];
private int itemCount;
// TODO 나머지 코드를 완성
}
실행 결과
장바구니 상품 출력
상품명:마늘, 합계:4000
상품명:상추, 합계:12000
전체 가격 합:16000
(3) 정답
Item
- 객체지향적으로 생각해보면 자신의 필드의 값(data)을 수정하거나 계산하는 로직(기능)은 자신이 가지고 있는것이 제대로 캡슐화를 하는 것임
- 즉, 속성과 기능이 같은 클래스에 있어야 나중에 수정이 생겨도 해당 클래스만 수정하면 되기 때문임
- 물론 getPrice, getQuantity를 생성하여 해당 값을 사용하는 곳에서 계산을 해도 나쁜 방법은 아니지만 일반적으로 자기 자신의 필드의 값은 자기자신이 계산하는 것이 좋음
- 항상 그런것은 아니지만 본인의 데이터는 본인이 수정하는 것이 가장 객체지향적으로 좋다고 할 수 있음
package access.ex;
public class Item {
private String name;
private int price;
private int quantity;
public Item(String name, int price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public String getName() {
return name;
}
public int getTotalPrice() {
return price * quantity;
}
}
ShoppingCart
- calculateTotalPrice() 메서드는 내부에서만 사용되므로 private 접근 제어자를 사용
package access.ex;
public class ShoppingCart {
private Item[] items = new Item[10];
private int itemCount;
public void addItem(Item item) {
if (itemCount == items.length) {
System.out.println("장바구니가 가득 찼습니다");
return;
}
items[itemCount] = item;
itemCount++;
}
public void displayItems() {
System.out.println("장바구니 상품 출력");
for (Item item : items) {
if (item != null) {
System.out.println("상품명: " + item.getName() + ", 합계 " + item.getTotalPrice());
}
}
System.out.println("전체 가격 합: " + calculateTotalPrice());
}
private int calculateTotalPrice() {
int totalPrice = 0;
for (int i = 0; i < itemCount; i++) {
Item item = items[i];
totalPrice += item.getTotalPrice();
}
return totalPrice;
}
}
'인프런 - 실전 자바 로드맵 > 실전 자바 - 기본편' 카테고리의 다른 글
패키지, 패키지(시작 / import) 패키지 규칙, 패키지 활용 (0) | 2024.12.28 |
---|---|
생성자, 생성자 - 필요한 이유, this, 생성자 - 도입, 기본 생성자, 생성자 - 오버로딩과 this(), 문제와 풀이 (1) | 2024.12.28 |
객체 지향 프로그래밍, 절차 지향 프로그래밍, 클래스와 메서드, 문제와 풀이 (0) | 2024.12.25 |
기본형과 참조형, 기본형 vs 참조형, 참조형과 메서드 호출, 변수와 초기화, null, NullPointerException, 문제와 풀이 (1) | 2024.12.21 |
클래스와 데이터, 프로젝트 환경 구성, 클래스가 필요한 이유, 클래스 도입, 객체 사용, 클래스, 객체, 인스턴스 정리, 배열 도입, 문제와 풀이 (2) | 2024.12.20 |