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

보람 - 1주차 #5

Merged
merged 5 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
![](https://velog.velcdn.com/images/bo-ram-bo-ram/post/f20aab35-b4e9-44d3-84df-f21740340e8d/image.png)


- 클라이언트가 클래스의 인스턴스를 얻는 방법
1. public 생성자
2. **static factory method(≠디자인 패턴)**

</br>

- static factory method의 장점
1. **이름을 가질 수 있다.**

→ 이름을 통한 반환될 객체의 특성 묘사 가능

2. **호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.**

→ 불필요한 객체 생성 피할 수 있음 ( 인스턴스 미리 생성 or 인스턴스 캐싱 후 재활용)

⇒ 인스턴스의 생명주기 철저히 통제 가능

( 통제의 이유 : 싱글턴 혹은 인스터스화 불가로 연결 가능, 동치의 인스턴스가 1개임을 보장)

3. **반환 타입의 하위 타입 객체를 반환할 수 있는 능력**
4. **입력 매개변수에 따라 매번 다른 클래스의 객체 반환 가능**
5. **정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨**

</br>

- static factory method의 단점
- 상속을 하려면 public이나 protected 생성자가 필요하여 정적 팩터 리 메서드만 제공하면 하위 클래스를 만들 수 없다.
- 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
</br>

</br>

> ☀️ 핵심정리</br>
정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다.
그렇다고 하더라도 정적 팩터리를 사용하는 게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
![](https://velog.velcdn.com/images/bo-ram-bo-ram/post/285fdab8-19b1-46cf-b0bb-a8e3c1e34585/image.png)

### 배경
선택적 매개변수가 많을 때 적절하게 대응하기 어렵다

</br>

### 선행적 해결 방법

1. ** 점층적 생성자 패턴**
: 필수 매개변수만 받는 생성자 ..>..>필수 매개변수 + 선택 매개변수 전체를 받는 생성자

```java
public class NutritionFacts {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택
private final int fat; // (g/1회 제공량) 선택
private final int sodium; // (mg/1회 제공량) 선택
private final int carbohydrate; // (g/1회 제공량) 선택

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 NutritionFactsdnt 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;
}
}
```
</br>

- 단점
1. 설정을 원하지 않는 매개변수까지 포함해야하고 어쩔 수 없이 그런 매개변수에도 값을 지정해줘야 한다.
2. 매개변수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵고 에러 발생 위험이 높다.
> 💡 확장이 어렵다



2. **Java Beans Pattern**

: 매개변수가 없는 생성자로 객체 생성 후 setter를 통해 매개변수 값 설정

```java
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 NutritionFactsO { }
// 세터 메서드들

public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCaloriesdnt(int val) { calories = val; }
public void setFatdnt(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
```

- 단점
1. 객체 하나를 만들기 위해 메서드를 여러개 호출해야 함
2. 객체가 완전히 생성되기 전까지 일관성이 무너진 상태
> 💡 일관성이 깨지고 불변으로 만들 수 없다.

<br/>

Builder Pattern

: 필수 매개변수로 생성자를 호출해 빌더 객체를 얻음

클래스 안에 정적 멤버 클래스로 생성

```jsx
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;

public static class Builder {
// 필수 매개변수
private final int servingSize;
private final int servings;
// 선택 매개변수 - 기본값으로 초기화한다.
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val)
{ calories = val; return this; }
public Builder fat(int val)
{ fat = val; return this; }
public Builder sodium(int val)
{ sodium = val; return this; }
public Builder carbohydrate(int val)
{ carbohydrate = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
```

- 장점
1. 빌더의 setter 메서드들은 빌더 자신을 반환하기 때문에 연쇄적으로 호출할 수 있다.
이런 방식을 메서드 호출이 흐르듯 연결된다.
2. 계층적으로 설계된 클래스와 함께 쓰기에 좋다
- 단점
1. 객체 생성 전 빌더를 먼저 만들어야한다
2. 매개변수가 4개 이상은 되어야 값어치가 있다.


> ☀️ 핵심정리 </br>
생성자나 정적 팩터리가 처리해야하 할 매개변수가 많다면 빌더 패턴을 선택하는게 더 낫다. 매개변수 중 다수가 필수가 아니거나 같은 타입이면 특히 더 그렇다. 빌더는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고 자바빈즈보다 훨씬 안전하다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
![](https://velog.velcdn.com/images/bo-ram-bo-ram/post/99944e65-a07a-4214-a1a1-df2f0ff5d832/image.png)

### 배경 : 싱글턴

싱글턴은 인스턴스를 오직 하나만 생성할 수 있는 클래스다. 이런 싱글턴 클래스는 호출 시 매번 인스턴스를 생성하지 않고 미리 생성해둔 인스턴스를 반환하기 때문에 DB의 Connection Pool에서도 사용하여 관리하는 것이 효율적이다

책에서는 이런 싱글톤을 만드는 방법으로 3가지를 소개하고 있다. 각각에 대해 알아보자.
<br>
- 싱글톤을 만드는 방법
1. **public static final 필드 방식의 싱글턴**

```java
public class Elvis {
**public static final Elvis INSTANCE = new Elvis();**
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
```

- 장점
1. 해당 클래스가 싱글턴임이 API에 드러남
2. 간결함

<br>
2. **정적 팩터리 방식의 싱글턴**

```java
public class Elvis {
private static final Elvis INSTANCE = new ElvisO;
private Elvis() { ... }
**public static Elvis getlnstance() { return INSTANCE; }**
public void leaveTheBuildingO { ... }

// 싱글턴임울 보장해주는 readResolve 메서드
private Object readResolve() {
// '진짜‘ Elvis를 반환하고, 가짜 Elvis는 가비지 컬렉터에 맡긴다.
return INSTANCE;
}
}
```

- 장점
1. api를 바꾸지 않고도 싱글턴이 아니게 변경 가능
2. 정적 팩터리를 제네릭 싱글턴 패턴으로 변경 가능
3. 정적 팩터리의 메서드 참조를 공급자로 사용 가능
<br>
3. **열거 타입 방식의 싱글턴 << 제일 추천**

```jsx
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
```

- 장점 : 간결함 및 추가 노력 없이 직렬화 가능
- 단점 : 만들려는 싱글턴이 Enum 외의 클래스를 상속해야 한다면 사용 불가
<br>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
![](https://velog.velcdn.com/images/bo-ram-bo-ram/post/61bc7333-4ca1-48ef-8976-77faa0737e2f/image.png)


### 배경

정적 메서드와 정적 필드는 OOP에서 미움 받는 형식이지만 아래와 같은 상황에선 쓰임새가 존재한다.

1. 기본 타입 값이나 배열 관련 메서드들을 모아놓을 때
2. 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드를 모아놓고 싶을 때
3. final 클래스와 관련한 메서드를 모아놓을 때
<br/>

하지만 이런 형식은 다음과 같은 이슈가 발생한다.


> 💡 **이슈** <br/>
정적 멤버만 담은 위와같은 클래스는 인스턴스로 만들어 쓰려고 설계한 것이 아니지만 컴파일러가 자동으로 기본 생성자를 만들어준다. 또 사용자는 이 생성자가 자동 생성된 것인지 구분이 불가하다.



이때 **추상 클래스**로 만들면 해결이 가능할까?
**아니다** 하위 클래스를 만들어서 인스턴스화를 하면 우회가 가능하다.
<br/>
그렇기 때문에 컴파일러가 기본 생성자를 만들지 못하도록 명시된 생성자가 없을 경우를 없애자.

이를 위해 **private 생성자를 추가하자**

```java
public class Utilityclass {
// 기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용
private UtilityClass() {
throw new AssertionError();
}
... // 나머지 코드는 생략
}
```

클래스가 인스턴스화는 막아 주지만, 생성자가 존재하는데 호출이 불가능하니까 **주석**을 달아주자.

또한 이렇게 작성한다면 **상속을 불가능**하게 하는 효과도 있다. 모든 생성자는 상위 클래스의 생성자를 호출하게 되는데 이를 private로 선언했으니 상위 클래스의 생성자에 접근하는 길이 막힌다.