An open API service indexing awesome lists of open source software.

https://github.com/hmmhmmhm/capsulable

πŸ’Š A module that helps developers easily encapsulate classes in nodejs.
https://github.com/hmmhmmhm/capsulable

encapsulation encapsule javascript nodejs object-oriented-javascript oop package private-variables

Last synced: about 1 month ago
JSON representation

πŸ’Š A module that helps developers easily encapsulate classes in nodejs.

Awesome Lists containing this project

README

          

## Capsulable

[![Build Status](https://travis-ci.org/hmmhmmhm/capsulable.svg?branch=master)](https://travis-ci.org/hmmhmmhm/capsulable)

```
순수 μžλ°”μŠ€ν¬λ¦½νŠΈ 클래슀 μΊ‘μŠν™” κ΅¬ν˜„ λͺ¨λ“ˆ
```
icon

μΊ‘μŠλŸ¬λΈ” λͺ¨λ“ˆμ€ μžλ°”μŠ€ν¬λ¦½νŠΈ ν΄λž˜μŠ€μ—μ„œ μ‰½κ²Œ μ ‘κ·Όμ œν•œμž κ°œλ…μ„ μ‚¬μš©ν•  수 있게 λ•μŠ΅λ‹ˆλ‹€. ν΄λ‘œμ € κ°œλ…μ„ 톡해 μΊ‘μŠν™” 및 은닉화 κ°œλ…μ„ κ΅¬ν˜„ν•©λ‹ˆλ‹€.

## μ„€μΉ˜λ°©λ²•

```
npm i capsulable --save
```

--------------------------------------

## μ™œ 이 λͺ¨λ“ˆμ΄ ν•„μš”ν•œκ°€μš”?

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ κΈ°λ³Έ λ³€μˆ˜κ°œλ…μ—λŠ” _ 와 __으둜 ν‘œν˜„ν•˜λŠ” prvate 및 protected λ³€μˆ˜ κ°œλ…μ΄ μ‘΄μž¬ν•˜μ§€λ§Œ, 타 ν΄λž˜μŠ€μ—μ„œ ν•΄λ‹Ή λ³€μˆ˜λ₯Ό μ½λŠ” 것을 λ§‰μ§€λŠ” λͺ»ν•©λ‹ˆλ‹€. λ˜ν•œ protected κ°œλ…μ˜ 경우 상속 이후 μ ‘κ·Όμ œν•œμ˜ κ°œλ…μ΄ μ•„μ§κΉŒμ§€ λͺ¨ν˜Έν•©λ‹ˆλ‹€. λ‚΄λΆ€ λ³€μˆ˜μ˜ 은닉성이 ν•„μš”ν•œ ν΄λž˜μŠ€μ—λŠ” 클래슀 λ³€μˆ˜μ— 보닀 λͺ…ν™•ν•œ μ ‘κ·Όμ œν•œμž κ°œλ…μ΄ ν•„μš”ν•©λ‹ˆλ‹€.

## κ·Έλƒ₯ Closure λ‚˜ Symbol 으둜 맀번 λ§Œλ“€λ©΄ λ˜μ§€ μ•Šλ‚˜μš”?

λ¬Όλ‘  κ·Έλ ‡κ²Œ 해도 상관은 μ—†μŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ 클래슀 μΈμŠ€ν„΄μŠ€ λ§ˆλ‹€ 각기 λ‹€λ₯Έ Private λ³€μˆ˜ 곡간을 λ§Œλ“€κ³  μ‹Άκ±°λ‚˜(`μ™ΈλΆ€μ—μ„œ μ ‘κ·Όν•  수 μ—†λŠ”`), 상속 이후에도 각기 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ λ‹€λ₯Έ Protected λ³€μˆ˜ 곡간을 λ§Œλ“€κ³  μ‹Άκ±°λ‚˜, νŠΉμ • νŒ¨ν‚€μ§€ μ‚¬μ΄μ—μ„œλ§Œ μ ‘κ·Όκ°€λŠ₯ν•œ Protected Static λ³€μˆ˜κ³΅κ°„μ„ 맀번 직접 λ§Œλ“ λ‹€λ©΄ λ‹€μ†Œ 힘이 λΉ μ§€λŠ” μž‘μ—…μΌ 수 μžˆμŠ΅λ‹ˆλ‹€. Capsulable 은 μ•ˆμ „ν•œ 은닉성과 ν™•μž₯μ„±, 그리고 상속 클래슀 및 νŒ¨ν‚€μ§€ κ°„μ˜ Protected 및 Protected Static λ³€μˆ˜ κ°œλ…μ„ μ œκ³΅ν•©λ‹ˆλ‹€.

--------------------------------------

## μΊ‘μŠν™” 된 클래슀 μ •μ˜λ°©λ²•

Capsulable 은 `class` 의 `constructor` ν•¨μˆ˜λ₯Ό ν†΅ν•΄μ„œ 데이터 ν•„λ“œλ₯Ό κ³΅μœ ν•©λ‹ˆλ‹€. (μ΄λŠ” 즉 Capsulable 이 μ΅œμ’…μ μœΌλ‘œ 항상 μžμ‹ 클래슀둜 ꡬ성 됨을 μ˜λ―Έν•©λ‹ˆλ‹€.) μΊ‘μŠν™” ν•˜κΈΈ μ›ν•˜λŠ” ν΄λž˜μŠ€λŠ” 사전에 Capsulable 을 ν†΅ν•΄μ„œ constructor μ—μ„œ 데이터 ν•„λ“œλ₯Ό 상속 λ°›μ•„μ•Όν•©λ‹ˆλ‹€. μ˜ˆμ‹œ 상 문법은 ES5둜 ν‘œκΈ°λ˜λ‚˜, Babel을 ν†΅ν•œ ES6 λ¬Έλ²•μœΌλ‘œ κ΅¬ν˜„ν•΄λ„ λ©λ‹ˆλ‹€.

### 클래슀 μ •μ˜ 예제

```js
// A.js
const Capsulable = require('capsulable')
const Field = Capsulable()

class A {
constructor(_field){
// 데이터 ν•„λ“œλ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€.
Field(this, _field)

// μ•„λž˜μ™€ 같은 μ½”λ“œλ₯Ό ν†΅ν•΄μ„œ
// 클래슀 λ‚΄ ν•¨μˆ˜ μž‘μ„± μ‹œ
// 데이터 ν•„λ“œμ— μ ‘κ·Ό κ°€λŠ₯ν•©λ‹ˆλ‹€.
Field(this).private
Field(this).protected
Field(this).protectedStatic
}
}

module.exports = A
```

### 클래슀 μΊ‘μŠν™” 예제
```js
// index.js
const Capsulable = require('./capsulable')
const A = require('./A')

// Capsulable에 클래슀 μ›ν˜•μ„ λ„£μœΌλ©΄
// μΊ‘μŠν™” 된 ν΄λž˜μŠ€κ°€ λ°˜ν™˜λ©λ‹ˆλ‹€.
const SharedA = Capsulable(A)

// μΊ‘μŠν™” 된 클래슀λ₯Ό ν†΅ν•΄μ„œ
// μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
let sharedA = new SharedA()
```
--------------------------------------

## Field λ³€μˆ˜ 접근법
```js
Field(this).private
Field(this).protected
Field(this).protectedStatic
```
Capsulable λͺ¨λ“ˆμ€ 각 ν΄λž˜μŠ€λ§ˆλ‹€ 데이터 ν•„λ“œλΌλŠ” 것을 μƒμ„±ν•˜λŠ”λ°, 이 데이터 ν•„λ“œμ—λŠ” `private`, `protected`, `protectedStatic` λ³€μˆ˜ν˜•νƒœ(`getter`)둜 μ œκ³΅λ©λ‹ˆλ‹€. 데이터 ν•„λ“œλŠ” 클래슀의 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ μƒμ„±λ˜λ―€λ‘œ 데이터 ν•„λ“œ μ ‘κ·Όμ‹œμ—λŠ” μΈμŠ€ν„΄μŠ€κ°€ ν•„λ“œμ— 인자둜 μ£Όμ–΄μ Έμ•Ό ν•©λ‹ˆλ‹€.

### Private ν•„λ“œ μ‚¬μš©λ²•
```js
Field(this).private.set(key, value) // => Boolean
Field(this).private.exist(key) // => Boolean
Field(this).private.get(key) // => Object
Field(this).private.getAll() // => Object
Field(this).private.remove(key) // => Boolean
Field(this).private.removeAll() // => Boolean
```
Private ν•„λ“œλŠ” 각 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ 격리된 곡간을 κ°€μ§€λ©° 타 μΈμŠ€ν„΄μŠ€κ°€ μ ‘κ·Όν•  수 없도둝 μ°¨λ‹¨λ©λ‹ˆλ‹€. κ°œλ°œμžκ°€ 같은 클래슀 λ‚΄μ—μ„œ `Field(this)` λ₯Ό λ‹€λ₯Έ μΈμŠ€ν„΄μŠ€μ—κ²Œ λ…ΈμΆœμ‹œν‚¬ 수 μžˆλŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ§€λ§Œ μ•ŠλŠ”λ‹€λ©΄ λ³€μˆ˜μ˜ 은닉성은 μœ μ§€λ  κ²ƒμž…λ‹ˆλ‹€.

### Protected ν•„λ“œ μ‚¬μš©λ²•
```js
Field(this).protected.set(className, key, value) // => Boolean
Field(this).protected.exist(className, key) // => Boolean
Field(this).protected.get(className, key) // => Object
Field(this).protected.getAll(className) // => Object
Field(this).protected.remove(className, key) // => Boolean
Field(this).protected.removeAll(className) // => Boolean
```
Protected ν•„λ“œλŠ” 각 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ 격리된 곡간을 κ°€μ§€λ©°, μ ‘κ·Όν•˜κ³ μž ν•˜λŠ” className 을 ν•¨μˆ˜ μ‚¬μš©μ‹œ 맨 처음 인자둜 μ œκ³΅ν•΄μ•Όν•©λ‹ˆλ‹€. Protected ν•„λ“œλŠ” 클래슀 상속이 λ°œμƒν–ˆμ„λ•Œ λΆ€λͺ¨ ν΄λž˜μŠ€μ™€ μžμ‹ 클래슀 μ‚¬μ΄μ—λ§Œ(λ˜ν•œ 같은 νŒ¨ν‚€μ§€) κ³΅μœ λ˜μ–΄μ•Ό ν•˜λŠ” 데이터 μš΄μš©μ— μ ν•©ν•©λ‹ˆλ‹€.

### Protected Static ν•„λ“œ μ‚¬μš©λ²•
```js
Field(this).protectedStatic.set(className, key, value) // => Boolean
Field(this).protectedStatic.exist(className, key) // => Boolean
Field(this).protectedStatic.get(className, key) // => Object
Field(this).protectedStatic.getAll(className) // => Object
Field(this).protectedStatic.remove(className, key) // => Boolean
Field(this).protectedStatic.removeAll(className) // => Boolean
```
Protected Static ν•„λ“œλŠ” 각 μΈμŠ€ν„΄μŠ€μ™€ 곡유된 곡간을 κ°€μ§€λ©°, μ ‘κ·Όν•˜κ³ μž ν•˜λŠ” className을 ν•¨μˆ˜ μ‚¬μš©μ‹œ 맨 처음 인자둜 μ œκ³΅ν•΄μ•Όν•©λ‹ˆλ‹€. Protected Static ν•„λ“œλŠ” λΆ€λͺ¨ν΄λž˜μŠ€μ™€ μžμ‹ν΄λž˜μŠ€κ°€ 같은 데이터λ₯Ό μš΄μš©ν•˜λ©΄μ„œ λ™μ‹œμ— λͺ¨λ“  클래슀 μΈμŠ€ν„΄μŠ€λ“€μ΄ 같은 데이터λ₯Ό κ³΅μœ ν•΄μ•Ό ν•˜λŠ” 데이터 μš΄μš©μ— μ ν•©ν•©λ‹ˆλ‹€.

--------------------------------------

## μΊ‘μŠν™” 된 νŒŒμƒ 클래슀 μ •μ˜λ°©λ²•

νŒŒμƒ 클래슀λ₯Ό μƒμ„±ν–ˆμ„ λ•Œ λΆ€λͺ¨ν΄λž˜μŠ€μ™€ μžμ‹ν΄λž˜μŠ€κ°€ `Protected` 와 `ProtectedStatic` ν˜•νƒœμ˜ 데이터 ν•„λ“œλ₯Ό κ³΅μœ ν•˜κ²Œ ν•˜λ €λ©΄ μ•„λž˜μ™€ 같은 방법을 톡해 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (상속을 진행해도 `Private` 데이터 ν•„λ“œλŠ” κ³΅μœ λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.)

### νŒŒμƒν΄λž˜μŠ€ μ •μ˜
```js
// B.js
const Capsulable = require('capsulable')
const A = require('./A')
const Field = Capsulable()

class B extends A {
constructor(_field){
// νŒŒμƒ ν΄λž˜μŠ€λŠ” constructor λ₯Ό ν†΅ν•΄μ„œ
// 받은 ν•„λ“œ 객체λ₯Ό super ν‚€μ›Œλ“œλ₯Ό ν†΅ν•΄μ„œ
// λ°˜λ“œμ‹œ λΆ€λͺ¨ 클래슀둜 λ„˜κ²¨μ•Όν•©λ‹ˆλ‹€.
super(_field)

// 데이터 ν•„λ“œλ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€.
Field(this, _field)
}
}

module.exports = B
```

### νŒŒμƒν΄λž˜μŠ€ μΊ‘μŠν™”
```js
// index.js
const Capsulable = require('./capsulable')
const B = require('./B')

// Capsulable에 클래슀 μ›ν˜•μ„ λ„£μœΌλ©΄
// μΊ‘μŠν™” 된 ν΄λž˜μŠ€κ°€ λ°˜ν™˜λ©λ‹ˆλ‹€.
const SharedB = Capsulable(B)

// μΊ‘μŠν™” 된 클래슀λ₯Ό ν†΅ν•΄μ„œ
// μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
let sharedB = new SharedB()
```
--------------------------------------

## 이미 μΊ‘μŠν™” 된 클래슀의 νŒŒμƒλ°©λ²•

이미 원 클래슀λ₯Ό Capsulable 둜 μΊ‘μŠν™” μ‹œν‚¨ 이후에 λ‹€μ‹œν•œλ²ˆ λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œ ν•΄λ‹Ή 클래슀λ₯Ό νŒŒμƒλ°›κ³  싢은 경우, μ•„λž˜μ™€ 같은 방법을 톡해 이λ₯Ό κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

```js
// C.js
const Capsulable = require('./capsulable')
const B = require('./B')
const SharedB = Capsulable(B)
const Field = Capsulable()

class C extends SharedB{
constructor(inherit){
// λΆ€λͺ¨ 클래슀인 Capsulable μ—μ„œ
// 데이터 ν•„λ“œλ₯Ό μ—­μœΌλ‘œ λ°›μ•„μ˜΅λ‹ˆλ‹€.
let hook = {}
super(hook)

// 데이터 ν•„λ“œλ₯Ό κ΅¬μ„±ν•©λ‹ˆλ‹€.
Field(this, hook.field)

// 이후 λ‹€μ‹œν•œλ²ˆ νŒŒμƒμ΄ κ°€λŠ₯ν•˜λ„λ‘,
// νŒŒμƒλμ„ μ‹œ 객체λ₯Ό λ³΅μ‚¬ν•΄μ€λ‹ˆλ‹€.
if(inherit) Capsulable(hook, inherit)
}
}

module.exports = C

// index.js
const Capsulable = require('./capsulable')
const C = require('./C')

// Capsulable에 클래슀 μ›ν˜•μ„ λ„£μœΌλ©΄
// μΊ‘μŠν™” 된 ν΄λž˜μŠ€κ°€ λ°˜ν™˜λ©λ‹ˆλ‹€.
const SharedC = Capsulable(C)

// μΊ‘μŠν™” 된 클래슀λ₯Ό ν†΅ν•΄μ„œ
// μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
let sharedC = new SharedC()
```
--------------------------------------

## νŒ¨ν‚€μ§€ 곡간 생성방법

Capsulable μ—λŠ” Java 의 Package κ°œλ…μ΄ λ―Έμ•½ν•˜κ²Œ κ΅¬ν˜„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μ•„λž˜μ²˜λŸΌ μ—¬λŸ¬ 클래슀λ₯Ό ν•œλ²ˆμ— μΊ‘μŠν™” ν•  경우, ν•΄λ‹Ή ν΄λž˜μŠ€λ“€μ˜ μΈμŠ€ν„΄μŠ€λ“€μ€ `Protected` 데이터 ν•„λ“œμ™€ `Protected Static` 데이터 ν•„λ“œκ°€ μžλ™μœΌλ‘œ κ³΅μœ λ©λ‹ˆλ‹€.

#### 주의: ν•˜λ‚˜μ˜ νŒ¨ν‚€μ§€μ— λ“±λ‘λ˜λŠ” μ—¬λŸ¬ 클래슀 μ›ν˜•λ“€μ€ μ„œλ‘œ 이름이 κ²ΉμΉ  수 μ—†μŠ΅λ‹ˆλ‹€.
```js
// index.js
const A = require('./A')
const B = require('./B')
const C = require('./C')

// Capsulable 에 인자둜
// 배열에 클래슀 μ›ν˜•λ“€μ„ λ‹΄μ•„ μ „λ‹¬ν•˜λ©΄
// 이λ₯Ό ν•˜λ‚˜μ˜ νŒ¨ν‚€μ§€λ‘œ κ΅¬μ„±ν•©λ‹ˆλ‹€.
let packages = Capsulable([A, B, C])

// μ•„λž˜μ™€ 같이 packages.* λ₯Ό 톡해
// μΊ‘μŠν™” 된 클래슀 μ›ν˜•μ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
let packA = new packages.A()
let packB = new packages.B()
let packC = new packages.C()
```

### νŒ¨ν‚€μ§€ 생성 이후 클래슀 λ‘œλ“œ
```js
// index.js
const A = require('./A')
const B = require('./B')
const C = require('./C')
const D = require('./A')

// Capsulable 에 인자둜
// 배열에 클래슀 μ›ν˜•λ“€μ„ λ‹΄μ•„ μ „λ‹¬ν•˜λ©΄
// 이λ₯Ό ν•˜λ‚˜μ˜ νŒ¨ν‚€μ§€λ‘œ κ΅¬μ„±ν•©λ‹ˆλ‹€.
let packages = Capsulable([A, B, C])

// packages._load ν•¨μˆ˜λ₯Ό ν†΅ν•΄μ„œ
// μ›ν•˜λŠ” 클래슀 μ›ν˜•μ„ νŒ¨ν‚€μ§€ 곡간 ꡬ성
// 이후에도 ν•΄λ‹Ή νŒ¨ν‚€μ§€μ— μΆ”κ°€μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
packages._load(D)

// packages.* λ₯Ό ν†΅ν•΄μ„œ
// μΆ”κ°€λœ 클래슀 μ›ν˜•μ— μ ‘κ·Ό κ°€λŠ₯ν•©λ‹ˆλ‹€.
let packD = new packages.D()
```

### νŒ¨ν‚€μ§€ 생성 이후 클래슀 μ–Έλ‘œλ“œ
```js
packages._load(D)
let packD = new packages.D()

// packages._unload ν•¨μˆ˜λ₯Ό ν†΅ν•΄μ„œ
// νŒ¨ν‚€μ§€μ— ν¬ν•¨λ˜μ–΄ μžˆλŠ” 클래슀 μ›ν˜•μ„
// ν•΄λ‹Ή νŒ¨ν‚€μ§€μ—μ„œ μ‚­μ œμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
packages._unload(D)

// 이미 νŒ¨ν‚€μ§€μ— μΆ”κ°€λœ 클래슀 μ›ν˜•μœΌλ‘œ
// 클래슀 μΈμŠ€ν„΄μŠ€κ°€ κ΅¬μ„±λœ 경우
// ν•΄λ‹Ή μΈμŠ€ν„΄μŠ€μ—λŠ” 영ν–₯이 μ—†μŠ΅λ‹ˆλ‹€.
```
--------------------------------------

## Final Class κ΅¬ν˜„

클래슀 μ›ν˜•μ˜ μΊ‘μŠν™” 이후 νŒŒμƒ 클래슀λ₯Ό 더 이상 λ§Œλ“€κΈΈ μ›μΉ˜ μ•ŠλŠ” 경우 Capsulable ν•¨μˆ˜ μ‚¬μš©μ‹œ 2번째 인자둜 final λͺ¨λ“œλ₯Ό μ μš©μ‹œν‚΄μœΌλ‘œμ¨ νŒŒμƒ ν΄λž˜μŠ€μ™€ 데이터 ν•„λ“œλ₯Ό κ³΅μœ ν•˜μ§€ μ•Šλ„λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (클래슀의 νŒŒμƒμ„ μ™„μ „νžˆ λ§‰μ§€λŠ” λͺ»ν•˜λ‚˜, 데이터 ν•„λ“œκ°€ κ³΅μœ λ˜λŠ” 것을 막을 μˆ˜λŠ” μžˆμŠ΅λ‹ˆλ‹€.)

```js
// index.js
const Capsulable = require('./capsulable')
const A = require('./A')

// 2번째 인자둜 final을 적으면
// νŒŒμƒν΄λž˜μŠ€μ™€ 데이터 ν•„λ“œλ₯Ό κ³΅μœ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
const SharedA = Capsulable(A, 'final')

// μΊ‘μŠν™” 된 클래슀λ₯Ό ν†΅ν•΄μ„œ
// μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
let sharedA = new SharedA()
```

--------------------------------------

## μž‘μ—… λͺ©λ‘

- [x] Private λ³€μˆ˜ κ΅¬ν˜„
- [x] Protected λ³€μˆ˜ κ΅¬ν˜„
- [x] Protected Static λ³€μˆ˜ κ΅¬ν˜„
- [x] 각 λ³€μˆ˜μ˜ 단계별 μ΄ˆκΈ°ν™” κ³Όμ • κ΅¬ν˜„
- [x] 톡합 데이터 ν•„λ“œ κ΅¬ν˜„
- [x] μƒμ†κΈ°λ°˜μ˜ μœ μΆœμ—†λŠ” 데이터 ν•„λ“œ 곡유 κ΅¬ν˜„
- [x] νŒ¨ν‚€μ§€κ°„ Protected 데이터 곡유 κ΅¬ν˜„
- [x] Helper λ₯Ό ν†΅ν•œ Private λ³€μˆ˜ μΈμžλ‹¨μΆ• κ΅¬ν˜„
- [x] μΊ‘μŠν™” 이후 상속 클래슀 μΆ”κ°€ 및 재 μΊ‘μŠν™” κ΅¬ν˜„
- [x] ν…ŒμŠ€νŠΈ μœ λ‹› κ΅¬ν˜„
- [x] assert κ°€λ³€λͺ¨λ“ˆν™” ν•˜μ—¬ 쒅속성 μ™„μ „ 배제 κ΅¬ν˜„
- [x] final 클래슀 κ΅¬ν˜„
- [ ] final λ³€μˆ˜ κ΅¬ν˜„
- [ ] μΊ‘μŠν™” 된 클래슀의 constructor κ°€λ³€λ³€μˆ˜ 인자 κ΅¬ν˜„
- [ ] μΊ‘μŠν™” 된 클래슀의 constructor κ°€λ³€λ³€μˆ˜μ˜ νŒŒμƒν΄λž˜μŠ€ 곡유 κ΅¬ν˜„
--------------------------------------

## LICENSE

MIT