Closed riyenas0925 closed 2 years ago
16pg
"자바빈즈패턴에서는 객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되지 전까지는 일관성(consistency)이 무너진 상태에 놓이게 된다."에서 일관성
의 의미? :
문단 아래 쪽의 "자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없으며 스레드 안정성을 얻으려면 프로그래머가 추가 작업을 해줘야만 한다."를 봤을 때 자바빈즈패턴은 스레드 안정적인 패턴이 아님을 알 수 있음.
=> 즉, setter가 있으면 언제든지 값을 바꿀 수 있으므로 멀티 쓰레딩 환경에서 값의 유효성을 보장하지 못한다는 뜻인 것 같다.
19pg 불변 : 어떠한 변경도 허용하지 않는다는 걸 의미
불변식 : 프로그램이 실행되는 동안, 혹은 정해진 기간 동안 반드시 만족해야 하는 조건을 의미
pg18
NutritionFacts 클래스는 왜 불변인가?
20pg
public abstract class Pizze{
// ..
abstract static class Builder<T extends Builder<T>> { ... }
// ..
}
에서 "재귀적 타입 한정" 이란? :
Builder<T extends Builder<T>>
에서 T가 자신을 포함하는 수식 Builder
static class Foo extends Pizza.Builder<Foo>
가 됨[질문] 16~17pg 자바 빈즈 패턴에서 일관성 이슈는 위의 질문-답변에서 해결 여기서 추가적으로 이를 완화하고자 freeze 메서드를 통해서 완화를 한다고 했는데 어떤 방식으로 하는것인지? 이게 어떤 측면에서 컴파일러가 보증할 방법이 없는것인지? 그래서 안 쓰이게 되는 부분이 무엇인지?
[질문] 18pg 빌더패턴에서 유효성 검사 코드는 생략했다고 했는데, 이 부분까지 확실히 추가해줘야하는 것인지 추가한다면 어떻게 추가해서 유효성 검사를 하는지 궁금함
[질문] 20pg 재귀적 타입 한정 위의 질문에서 개념과 코드적으로 설명했는데 좀 더 들어가서 제너릭 타입이 어떻게 작용되면서 쓰이는지 그리고 제너릭 타입에 대해서 다른 예시와 함께 구체적인 정의를 다시 짚고 넘어가고 싶음(제너릭 부분이 좀 애매하게 이해됨)
[질문] 20pg 책에서 설명한 시뮬레이트한 셀프 타입에 대해서 좀 더 추가적인 예시가 궁금함, 책에 설명과 덧붙여서
[질문] 21pg 공변 반환 타이핑에 대한 추가적인 예시와 책에서 말한 정의를 좀 더 구체화해서 짚고 넘어가고 싶음
[질문] 16~17pg 자바 빈즈 패턴에서 일관성 이슈는 위의 질문-답변에서 해결 여기서 추가적으로 이를 완화하고자 freeze 메서드를 통해서 완화를 한다고 했는데 어떤 방식으로 하는것인지? 이게 어떤 측면에서 컴파일러가 보증할 방법이 없는것인지? 그래서 안 쓰이게 되는 부분이 무엇인지?
자바 빈즈 패턴이 일관성 이슈가 있음 이는 객체를 완성해도 setter로 언제든 내용이 변경될 여지를 말함, 이러면 불변성 유지가 힘듬
그래서 freeze 메소드를 통해서 불변성을 유지할 수 있다고 하는데 그 코드를 본다면 아래와 같음
public class A {
private String a;
private int b;
private boolean freeze = false;
public A() {}
public void setA(String a) {
if (isFreeze()) {
throw new AssertionError("완성된 객체는 불변성을 유지해야 합니다.");
}
this.a = a;
}
public String getA() {
return a;
}
public void setB(int b) {
if (isFreeze()) {
throw new AssertionError("완성된 객체는 불변성을 유지해야 합니다.");
}
this.b = b;
}
public int getB() {
return b;
}
public boolean isFreeze() {
return freeze;
}
public void freeze() {
this.freeze = true;
}
}
A a = new A();
a.setA("A");
a.setB(2);
a.freeze();
하지만 저렇게 했다고 하여도 freeze 메소드를 확실히 호출했는지 컴파일러가 보증할 방법이 없기 때문에 즉, 만약 멀티 쓰레딩 환경일 경우 단일 환경이 아니라면 보장이 안된다고 볼 수 있음, 그래서 안 쓰는 것 같음
[질문] 18pg 빌더패턴에서 유효성 검사 코드는 생략했다고 했는데, 이 부분까지 확실히 추가해줘야하는 것인지 추가한다면 어떻게 추가해서 유효성 검사를 하는지 궁금함
빌더패턴 자체는 책에서 말해준대로 쓰면 되고 빌더 패턴은 실제로 자연스럽게 여러번 썼음, 개발하는 과정에 있어서
유효성 검사를 추가한다면 빌더의 생성자나 메서드에서 검사하고 build()가 호출하는 생성자에서 불변식 검사등 추가할 수 있음
TargetObject build() {
TargetObject res = new TargetObject();
res.setProperty1();
res.setProperty2();
validate(res); // This call may throw an exception
return res;
}
void validate(TargetObject obj) {
if (...) {
throw new IllegalStateException();
}
}
TargetObject build() {
TargetObject res = new TargetObject();
res.setProperty1();
res.setProperty2();
if (...) {
throw new IllegalStateException();
}
return res;
}
[질문] 20pg 재귀적 타입 한정 위의 질문에서 개념과 코드적으로 설명했는데 좀 더 들어가서 제너릭 타입이 어떻게 작용되면서 쓰이는지 그리고 제너릭 타입에 대해서 다른 예시와 함께 구체적인 정의를 다시 짚고 넘어가고 싶음(제너릭 부분이 좀 애매하게 이해됨)
재귀적 타입 한정은 자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정하는 케이스임
이를 이해하기 쉽게 Comparable과 비교를 해본다면 static <T extends Comparable<T>>
가 있는데 이는 자신과 비교될 수 있는 모든 타입 T 즉, 모든 타입 T는 자신과 비교할 수 있다는 것임
이는 Comparable을 예시로 활용성에 대해서 좀 더 생각을 해본다면 앞서 Wrapper 타입의 경우 이를 비교 연산을 한다고 할 때 비교연산자를 사용할 수 없기 때문에 이를 재귀적 타입 한정을 활용해서 쓸 수 있음
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
여기서 그러면 제네릭을 더 따져본다면 좀 더 정의를 해본다면 데이터 형식에 의존하지 않고 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법이라고 볼 수 있음
이러면 제네릭은 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것임, 그렇다면 위에서 Comparable이나 Builder나 지정은 외부에서 해주는 것이어서 Comparable은 봤듯이 비교연산이 Wrapper Class에서는 안되기 때문에 비교연산을 하고자 하는 Wrapper Class가 정해지는 것이고 Builder의 경우도 Builder를 구체적으로 구현하는 방향에 있어서 책에서 NyPizza, Calzone의 조건이 다르기 때문에 제네릭을 통해서 각각 맞춰서 Builder가 될 수 있도록 해줄 수 있었던 것임
[질문] 20pg 책에서 설명한 시뮬레이트한 셀프 타입에 대해서 좀 더 추가적인 예시가 궁금함, 책에 설명과 덧붙여서
코드로 본다면 아래와 같이 Builder와 self메서드가 그럼
public abstract class Pizza{
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
// 추상 클래스는 추상 Builder를 가진다. 서브 클래스에서 이를 구체 Builder로 구현한다.
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// 하위 클래스는 이 메서드를 overriding하여 this를 반환하도록 해야 한다.
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone();
}
}
public class NyPizza extends Pizza { public enum Size { SMALL, MEDIUM, LARGE } private final Size size;
public static class Builder extends Pizza.Builder
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) { super(builder); size = builder.size; } }
public class Calzone extends Pizza { private final boolean sauceInside;
public static class Builder extends Pizza.Builder
public Builder sauceInside() {
sauceInside = true;
return this;
}
@Override public Calzone build() {
return new Calzone(this);
}
@Override protected Builder self() { return this; }
}
private Calzone(Builder builder) { super(builder); sauceInside = builder.sauceInside; } }
- 이러면 메서드 연쇄를 지원할 수 있다는 것인데 이는 아래와 같이 형변환을 생각하지 않고도 빌더를 사용하는 것을 의미함
```java
public class Main {
public static void main(String[] args) {
NYPizza pizza = new NYPizza.Builder(SMALL)
.addTopping(SAUSAGE)
.addTopping(ONION)
.build();
Calzone calzone = new Calzone.Builder()
.addTopping(HAM)
.sauceInside()
.build();
}
}
[질문] 21pg 공변 반환 타이핑에 대한 추가적인 예시와 책에서 말한 정의를 좀 더 구체화해서 짚고 넘어가고 싶음
이 공변 반환 타이핑의 예시는 Pizza 예시가 정확한 예시임 즉, self를 구현할 때 각각 Builder로 Calzone, NyPizza를 return this;를 하기 때문에 반환형을 자기 자신으로 오버라이딩 하는 경우인 것임
하위타입을 반환을 해도 문제가 없는 것이고 더 좁은 타입 즉 하위 타입으로 교체될 수 있는 경우를 말하는 것임
[질문] 21pg 공변 반환 타이핑에 대한 추가적인 예시와 책에서 말한 정의를 좀 더 구체화해서 짚고 넘어가고 싶음
Producer의 method에서 SuperType(Object) 리턴한다면, IntegerProducer에서 Override하는 method에서는 SubType(Integer)를 리턴해도 된다
public class Producer {
public Object produce(String input) {
Object result = input.toLowerCase(Locale.ROOT);
return result;
}
}
public class IntegerProducer extends Producer {
@Override
public Integer produce(String input) {
return Integer.parseInt(input);
}
}
필수 매개변수만 받는 생성자, 필수 매개변수와 선택 매개변수 1개를 받는 생성자, 선택 매개변수를 2개까지 받는 생성자... 형태로 선택 매개변수를 전부다 받는 생성자까지 늘려가는 방식
매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. 코드를 읽을 때 각 값의 의미가 불명확하고 타입이 같은 매개변수가 연달아 늘어서 있으면 찾기 어려운 버그로 이어질 수 있다.
public class NutritionFacts {
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class Main {
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
}
}
메개변수가 없는 생성자로 객체를 만든 후 setter 메서드를 호출해 원하는 매개변수의 값을 설정하는 방식
자바 빈즈 패턴에서는 객체 하나를 만들려면 메서드를 여러 개 호출해야 하고, 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓이게 된다. 이런 문제 때문에 자바빈즈 패턴에서는 클래스를 불변으로 만들 수 없으며 스레드 안정성을 얻으려면 프로그래머가 추가 작업을 해줘야 한다.
public class NutritionFacts {
private int servingSize = -1;
private int servings = -1;
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() {
}
public void setServingSize(int servingSize) {
this.servingSize = servingSize;
}
public void setServings(int servings) {
this.servings = servings;
}
public void setCalories(int calories) {
this.calories = calories;
}
public void setFat(int fat) {
this.fat = fat;
}
public void setSodium(int sodium) {
this.sodium = sodium;
}
public void setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
}
}
public class Main {
public static void main(String[] args) {
NutritionFacts nutritionFacts = new NutritionFacts();
nutritionFacts.setCalories(100);
nutritionFacts.setServingSize(240);
nutritionFacts.setServings(8);
nutritionFacts.setSodium(35);
nutritionFacts.setCarbohydrate(27);
}
}
[질문] 18pg 빌더패턴에서 유효성 검사 코드는 생략했다고 했는데, 이 부분까지 확실히 추가해줘야하는 것인지 추가한다면 어떻게 추가해서 유효성 검사를 하는지 궁금함
https://stackoverflow.com/questions/38173274/builder-pattern-validation-effective-java
아이템 : 생성자에 매개변수가 많다면 빌더를 고려하다