프로그램을 설계할 때 방생한 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의
규약
형태로 만들어 놓은 것을 의미한다.
하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴으로, 인스턴스를 생성할 때 드는 비용이 줄어들지만, 의존성이 높아진다는 단점이 존재한다.
private static EmployeeMgr manager;
public static EmployeeMgr getInstance() {
if(manager == null) {
manager = new EmployeeMgr();
}
return manager;
}
EmployeeMgr manager = EmployeeMgr.getInstance();
getInstance()로 같은 인스턴스만을 받아와서 사용한다.
public class DBConn
{
private static Connection dbConn;
public static Connection getConnection() throws ClassNotFoundException, SQLException
{
if (dbConn == null)
{
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String user = "lee";
String pwd = "youngjun";
Class.forName("oracle.jdbc.driver.OracleDriver");
dbConn = DriverManager.getConnection(url, user, pwd);
}
return dbConn;
}
public static void close() throws SQLException
{
if (dbConn != null)
{
if (!dbConn.isClosed())
{
dbConn.close();
}
}
dbConn = null;
}
}
자바에서 DB연결을 위해 driverManage를 통해 Connection객체를 만들어 사용하는데, 이를 만들기 위한 getConnection
함수는 connection이 null
이 아닐 때 같은 객체인 dbConn을 항상 반환할 것이다.
object로 선언된 클래스는 클래스의 정의와 함께 인스턴스가 생성되며 싱글톤으로 하나만 생성된다.
따라서 별도의 객체를 생성하지 않고 바로 사용한다.
- 속성을 초기화하기 위해 init블록을 사용할 수 있다.
- 또한 object는 바로 선언되지만, 안의 속성들은 호출시에 초기화되도록 하기 위해
by lazy
를 사용할 수 있다.- 생성자를 사용할 수 없다.
object RetrofitServiceImpl {
private const val BASE_URL = ""
private val retrofit:Retrofit = Retrofit.Builder()
.baserUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
클래스 내부에서 선언되는 클래스로 생성자로 parameter 를 받는 경우에 사용한다.
- 객체를 생성하여 사용하며, 이 객체들의 메모리 주소값은 동일하다.(싱글톤이다.)
- companion object가 있는 클래스를 상속받는 클래스에서는 companion object가 가려진다.
class MySingleton private constructor() {
// 파라메터를 받는 싱글톤 클래스를 만들려면 companion object를 이용한다.
companion object {
private var instance: MySingleton? = null
private lateinit var context: Context
fun getInstance(_context: Context): MySingleton {
return instance ?: synchronized(this) {
instance ?: MySingleton().also {
context = _context
instance = it
}
}
}
}
fun printMsg(msg: String) {
Log.d("MySingleton", "msg: $msg")
}
}
객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 주요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴이다.
객체 지향 디자인 패턴의 기본 원칙은 확장에 있어서는 열려 있어야 하며, 수정에 있어서는 닫혀 있어야 한다는 것이다. (OCP, Open Closed Principle)
여기서 수정에 있어서 닫혀 있어야 한다는 말에 주목 해보자. 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경 할 수 있어야 한다. 때문에, 수정이 일어날 가능성이 큰 부분과 그렇지 않은 부분을 분리하는 것이 좋다.
객체는 속성과 함수가 변경, 또는 추가 될 수 있다. 이에 따라 객체의 생성을 담당하는 코드는 변경의 가능성이 높다. 객체의 생성을 담당하는 클래스를 한 곳에서 관리하여 결합도를 줄이기 위하여 팩토리 패턴이 나타나게 된 것이다.
팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하게 만드는 패턴이다.
enum CoffeeType {
LATTE, ESPRESSO
}
abstract class Coffee {
protected String name;
public String getName() {
return name;
}
}
class Latte extends Coffee {
public Latte() {
name = "latte";
}
}
class Espresso extends Coffee {
public Espresso() {
name = "Espresso";
}
}
class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch (type) {
case LATTE:
return new Latte();
case ESPRESSO:
return new Espresso();
default:
throw new IllegalArgumentException("Invalid coffee type: " + type);
}
}
}
추상 팩토리 패턴은 팩토리 메서드 패턴을 한번 더 추상화 한 것으로 많은 수의 연관된 서브 클래스를 특정 그룹으로 묶어 한번에 교체할 수 있도록 만든 디자인 패턴이다.
팩토리 메서드 패턴과 유사하지만, 팩토리를 만드는 상위 팩토리 클래스
가 존재한다. 쉽게 말해 팩토리를 만드는 팩토리 클래스를 만들어 더 캡슐화를 한다.
https://victorydntmd.tistory.com/300
안드로이드에서는 viewmodel에 인자를 넣어 초기화해야하는 경우에 Factory 패턴을 자주 사용한다.
물론 현재는 Koin 혹은 Hilt를 통해 Module로 선언된 객체들에 한해 주입을 해주면 훨씬 편리하긴 하다
class MainViewModel @Inject constructor(
private val repository: MoviesRepository
)
하지만 이를 사용하지 않으면서 인자를 가진 viewModel를 생성하려면 factory 패턴을 자주 사용한다.
뷰모델을 사용하는 이유는 activity의 생명주기에 의존하지 않고(완전히 finish되는게 아니라면) 데이터를 관리하기 위함인데 activity에서 데이터를 주게 되면 관리의 의미가 없으니까!
인자로 String값을 받는 viewmodel를 팩토리 패턴으로 만드는 예시이다.
class FriendViewModelFactory(private val friendName: String?) :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(FriendViewModel::class.java)) {
return FriendViewModel(friendName) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
기존의 KTX로 viewModel을 호출하는 delegation 방식을 그대로 쓰면서 아래와 같이 String을 인자로 받는 뷰모델을 초기화 할 수 있다.
val friendViewModel: FriendViewModel by viewModels {
FriendViewModelFactory("leeyoungjun")
}
혹은 고전적인 방식으로 ViewModelProvider를 사용하여 초기화 할 수도 있다.
var noParamAndroidViewModel = ViewModelProvider(this, ViewModelProvider.FriendViewModelFactory("leeyoungjun"))
.get(FriendViewModel::class.java)
정신건강을 위해서 Koin이나 Hilt를 쓰자^^
즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴
- 자바스크립트는 접근 제어자가 존재하지 않고 전역 범위에서 스크립트가 실행되기 때문에 노출모듈 패턴을 통해 private와 public 접근 제어자를 구현
const pukuba = (() => {
const a = 1
const b = () => 2
const public = {
c : 2,
d : () => 3
}
return public
})()
console.log(pukuba)
console.log(pukuba.a)
// { c: 2, d: [Function: d] }
// undefined
pubuka는 즉시 실행 함수로 선언하면 바로 c, d를 return 하게 되어있다. 여기서 pukuba.c, pukuba.d
와 같이 public의 방식으로 변수를 받아올 수 있다. 이에 반해 a, b는 private과 같이 외부에서는 접근할 수 없다.
클래스에 정의된 함수에서 접근 가능하며 자식 클래스와 외부 클래스에서 접근 가능한 범위
클래스에 정의된 함수에서 접근 가능, 자식 클래스에서 접근 가능하지만 외부 클래스에서 접근 불가능한 범위
클래스에 정의된 함수에서 접근 가능하지만 자식 클래스와 외부 클래스에서 접근 불가능한 범위
- 코틀린의 singleton
https://bacassf.tistory.com/59- 코틀린 object lazy
https://kotlinworld.com/166- 코틀린 koin, Hilt로 Viewmodel 인자 전달
https://black-jin0427.tistory.com/393- Factory 패턴으로 뷰모델 생성
https://velog.io/@nagosooo/ViewModel- Factory 패턴으로 뷰모델을 만드는 이유
https://bb-library.tistory.com/271- 추상 팩토리, 팩토리 메서드 패턴
https://velog.io/@ellyheetov/Factory-Pattern
정리가 잘 된 글이네요. 도움이 됐습니다.