Skip to content

Commit

Permalink
Merge pull request #5 from SOPT-server/boram/week1
Browse files Browse the repository at this point in the history
보람 - 1주차
  • Loading branch information
unanchoi authored Apr 21, 2024
2 parents 939ca68 + cf7c959 commit ce71bc4
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 0 deletions.
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 NutritionFactsint servingSize, int servings) {
this(servingSize, servings, 0);
public NutritionFactsint 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로 선언했으니 상위 클래스의 생성자에 접근하는 길이 막힌다.

0 comments on commit ce71bc4

Please sign in to comment.