728x90
반응형

Composite Pattern 이란?

- 객체를 트리구조로 구성해서 부분-전체 계층구조를 구현한다.

- 클라이언트에서 개별 객체와 복합 객체를 똑같은 방법으로 다룰 수 있다.

 

윈도우의 디렉토리 구조를 생각하면 쉽다. 폴더에는 폴더 또는 파일이 들어갈 수 있으며 개별 객체(파일), 복합 객체(폴더)를 똑같은 방법으로 다룰 수 있는 구조가 Composite Pattern 이라고 생각하면 된다!

 

Component

- 개별 객체 뿐만 아니라 이런한 개별 객체들을 계층 구조로 포함하는 복합 객체를 나타내는 인터페이스 또는 추상 클래스

- 모든 클래스에 공통적인 행위에 대해 기본 기능을 구현할 수 있다.

Leaf (개별 객체)

- 개별 객체에 해당되는 컴포넌트

Composite (복합 객체)

- 다른 컴포넌트를 포함할 수 있는 컴포넌트

- 개별 객체 또는 다른 복합 객체를 포함할 수 있음

 

예시

맥도날드에서 메뉴판을 보면 불고기 버거 단품, 감자 튀김, 불고기 버거 세트 메뉴, 패밀리 세트 메뉴 등 다양한 메뉴가 존재한다. 이 때, 햄버거와 감자튀김은 단일 메뉴로서 역할을 할 수도 있고 버거 세트 메뉴로서 하나의 메뉴에 포함될 수도 있다. 이 때, 햄버거는 개별 객체(Leaf)로서 버거 세트 메뉴는 복합 객체(Composite)으로 비유할 수 있고 손님은 이 둘을 똑같은 방식으로 다룰 수 있다.

 

Component

public interface MenuComponent {
    public default void add(MenuComponent comp){
        throw new UnsupportedOperationException();
    }
    public default void remove(MenuComponent comp){
        throw new UnsupportedOperationException();
    }
    public default MenuComponent getChild(int i){
        throw new UnsupportedOperationException();
    }
    public default String getName(){
        throw new UnsupportedOperationException();
    }
    public default String getDescription(){
        throw new UnsupportedOperationException();
    }

    public default double getPrice(){
        throw new UnsupportedOperationException();
    }
    public default boolean isVegetarian(){
        throw new UnsupportedOperationException();
    }
    public default void print(){
        throw new UnsupportedOperationException();
    }
}

 

Leaf

public class MenuItem implements MenuComponent{
    String name;
    String description;
    boolean vegetarian;
    double price;

    public MenuItem(String name, String desc, boolean vege, double price){
        this.name = name;
        this.description = desc;
        this.vegetarian = vege;
        this.price = price;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public boolean isVegetarian() {
        return vegetarian;
    }

    @Override
    public double getPrice() {
        return price;
    }

    @Override
    public void print() {
        System.out.println(" "+getName());
        if(isVegetarian()){
            System.out.println("(v)");
        }
        System.out.println(", "+getPrice());
        System.out.println("    -- "+getDescription());
    }
}

 

Composite

public class Menu implements MenuComponent{
    ArrayList<MenuComponent> menuComponents = new ArrayList();
    String name;
    String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public void add(MenuComponent comp) {
        menuComponents.add(comp);
    }

    @Override
    public void remove(MenuComponent comp) {
        menuComponents.remove(comp);
    }

    public void print() {
        System.out.print("\n" + getName());
        System.out.println(", " + getDescription());
        System.out.println("---------------------");
        Iterator it = menuComponents.iterator();
        while (it.hasNext()) {
            MenuComponent component
                    = (MenuComponent) it.next();
            component.print();
        }
    }

}

 

728x90
반응형
728x90
반응형

빌더 패턴

빌더 패턴은 복잡한 객체 생성 과정을 단순화하고 객체의 다양한 속성을 설정하는 방법을 제공하는 디자인 패턴 이다.

 

이해가 잘 되지 않는다. 바로 예제를 살펴보자

 


빌더 패턴 예제

콜라 같은 식품의 영양 정보표를 만들어보자. 이 표에는 요구되는 필수 정보와 넣어도 되고 넣지 않아도 되는 선택적 정보를 포함한다.

필수 정보

- 1회 분량(serving size), 총 분량(servings) ...

선택 정보

- 지방(total fat), 포화지방(saturated fat), 트랜스 지방(trans fat), 나트륨(sodium)...

 

 

이 때, 선택 정보에 대해 생성자를 여러개 만들어 오버로딩할 수 도 있지만 너무 코드가 길어지고  파라미터의 순서나 개수를 파악하기 힘들어 문제가 생긴다. 이 때, 빌더 패턴을 사용한다. 코드를 살펴보자.

 

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 {
        // Required parameters
        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);
        }

    }
    public int getCalories(){
        return calories;
    }
    NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

- Builder라는 빌더 클래스를 생성한다. 파라미터로는 필수 정보인 servingSize와 servings를 받는다.

- 선택 정보는 setter의 형태로 메소드를 작성해준다.(set은 붙이지 않아도 된다.) 객체를 생성할 때 포함할지 하지 않을지 선택권을 준다.

- build() 메소드로 객체를 생성하면서 원하는 정보만 가진 객체를 생성할 수 있게 된다.

 

public class NutritionFactsMain {
    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240,8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27)
                .build();
        System.out.println(cocaCola.getCalories());
    }
}

- 넣고 싶은 선택 정보만 포함해서 객체를 생성함을 볼 수 있다.

 


정리

- 빌터 패턴은 객체 생성을 직접 하지 않고 필수 요소를 전달한 후 생성자를 호출해서 빌더 객체를 생성한다.

- 빌더 객체에 선택 요소에 대하 setter와 비슷한 메소들을 호출한다.

- build() 메소드를 호출해서 원하는 immutable 객체를 생성한다.

728x90
반응형
728x90
반응형

이 글은 안드로이드 공식문서를 참고하여 작성했다.

https://developer.android.com/codelabs/basic-android-kotlin-training-repository-pattern?hl=ko#0 

 

저장소 패턴  |  Android Developers

저장소 패턴을 사용하여 기존 앱에서 캐싱을 구현합니다.

developer.android.com

 

Repository Pattern 이란?

데이터 레이어를 앱의 나머지 부분에서 분리하는 디자인 패턴이다.

- 데이터 레이어는 UI와 별도로 앱의 데이터와 비지니스 로직을 처리하는 앱 부분을 나타내며 앱의 나머지 부분에서 이 데이터에 액세스할 수 있도록 일관된 API를 노출한다.

- UI가 사용자에게 정보를 제공하는 동안 데이터 레이어에는 네트워킹 코드, Room 데이터베이스, 오류 처리, 데이터를 읽거나 조작하는 코드를 포함한다.

 

뭔 말이냐... 그림을 보며 이해해보자!

데이터는 Room Data, Retorfit, Firebase 등 다양한 곳에서 데이터를 받을 수 있다. 그래서 어떤 형태로 데이터가 접근을 해도 Repository 클래스를 통해 캡슐화해서 깔끔하게 접근하겠다는 거지!!

 

즉, 데이터가 어디서 왔든지 동일한 Repository 인터페이스로 접근하겠다는 것이다!!!


코드 예제

작업 1. 저장소 만들기

- 오프라인 캐시를 관리할 저장소를 만들어보자!!

- 베이스 코드는 위의 안드로이드 공홈에서 다운로드 받자.

 

저장소 추가 및 데이터베이스에서 데이터 검색

VideosRepository.kt

class VideosRepository(private val database:VideosDatabase){

    // LiveData 객체를 만들어 데이터베이스에서 동영상 재생목록을 읽는다.
    val videos: LiveData<List<DevByteVideo>> = Transformations.map(database.videoDao.getVideos()){
        it.asDomainModel()
    }
    suspend fun refreshVideos(){
        // 코루틴 컨텍스트를 Dispatchers.IO로 전환하여 네트워크 및 데이터베이스 작업을 실행
        withContext(Dispatchers.IO){
            // 네트워크에서 재생목록을 가져온 후 재생목록을 RoomDB에 저장
            val playlist = DevByteNetwork.devbytes.getPlaylist()
            // insertAll() DAO 메소드를 호출하여 네트워크에서 검색된 재생목록을 전달
            // asDatabaseModel() 확장 함수를 사용하여 재생목록을 데이터베이스 객체에 매핑
            database.videoDao.insertAll(playlist.asDatabaseModel())
        }
    }
}

- Retrofit 서비스 인스턴스인 DevByteNetwork를 사용해 playlist를 네트워크에서 가져온다.

- 재생목록을 데이터베이스에 저장한다.

- videos라고 LiveData 객체를 만들어 데이터베이스에서 재생목록을 읽는다.

 

 

작업 2. 새로고침 전략을 사용하여 저장소 통합

- 저장소를 ViewModel과 통합

- 네트워크에서 직접 가져오지 않고 Room 데이터베이스의 동영상 재생목록을 표시

 

DevByteViewModel.kt

private val videosRepository = VideosRepository(getDatabase(application))

// Network에서 부터 데이터를 가져오던 것에서 Repository에서 가져오게 변경
private fun refreshDataFromRepository() = viewModelScope.launch {
    viewModelScope.launch {
        try{
            videosRepository.refreshVideos()
            _eventNetworkError.value = false
            _isNetworkErrorShown.value = false
        }catch (networkError:IOException){
            if(playlist.value.isNullOrEmpty())
                _eventNetworkError.value=true
        }
    }
}

 

- 네트워크에서 데이터를 받아오던 코드를 위와 같이 Repository에서 받아오게끔 수정한다.

- 한 번 실행 후 비행기 모드로 설정해서 앱을 다시 실행하면 오프라인 캐시에서 가져와 에러 없이 재생목록을 가져올 수 있다.

728x90
반응형
728x90
반응형

데코레이터 패턴

 

늘 그랬듯이 클래스 다이어그램부터 살펴보자.

천천히 알아보자.

 

데코레이터 패턴이란?

- decorate: 장식하다.

- 객체에 추가 요소를 동적으로 더할 수 있다.

- 서브클래스를 만들 때보다 훨씬 유연하게 기능을 확장할 수 있다.

 


 

이해를 돕기 위해 예를 들어보자. 헤드 퍼스트 디자인 패턴 책의 예제를 가져왔다.

 

예제 : 스타버즈 커피

 

문제

빠른 성장세에 다양한 음료들을 모두 포괄하는 주문 시스템을 갖추려고 하는 스타버즈 커피숍이 있다. 초기 시스템은 아래와 같다.

문제점은 여기서 발생한다. 시스템은 구축해놓았는데 모카나 우유나 휘핑 크림 같은 추가 토핑을 추가해야 할 때, 또는 가격이 변동될 때 상속받는 클래스가 아래와 같이 너무 많아진다는 것이다. (ex 휘핑 크림을 얹은 모카 에스프레소 클래스... 이런 것 까지 따로 구현해야 한다.)

이를 데코레이터 패턴을 적용하여 개선시켜보자!!

이런 식으로 적용하면 "휘핑 크림은 얹고 모카를 넣은 DarkRoast 클래스" 를 만들 때 이 자체의 클래스를 만드는 것이 아니라 구성하는 객체들이 감싸주기만 하면 된다. 아래처럼 말이다.

 


코드로 살펴보자.

Beverage.java

public abstract class Beverage {
    String description = "제목 없음";

    public String getDescription(){
        return description;
    }
    public abstract double cost();

}

 

DarkRoast.java

public class DarkRoast extends Beverage{

    public DarkRoast(){
        description = "다크 로스트";
    }

    @Override
    public double cost() {
        return 0.99;
    }
}

 

CondimentDecorator.java

public abstract class CondimentDecorator extends Beverage{

    public abstract String getDescription();
    @Override
    public abstract double cost();
}

 

Moch.java

public class Mocha extends CondimentDecorator{
    Beverage beverage;

    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return "모카 " + beverage.getDescription();
    }

    @Override
    public double cost() {
        return 0.2 + beverage.cost();
    }
}

 

Whip.java

public class Whip extends CondimentDecorator{
    Beverage beverage;

    public Whip(Beverage beverage){
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return "휘핑 " + beverage.getDescription();
    }

    @Override
    public double cost() {
        return 0.1 + beverage.cost();
    }
}

 이제 처음 봤던 클래스 다이어그램을 보며 마무리하자.

 

- Component : 각 구성요소는 직접 쓰일 수도 있고 데코레이터로 감싸져서 쓰일 수도 있다 (클래스 or 인터페이스)

- ConcreteComponent : 새로운 행동을 동적으로 추가 (DarkRoast)

- Decorator : 자신이 장식할 구성요소와 같은 인터페이스 또는 추상 클래스의 역할 (CondimentDecorator)

- ConcreteDecorator : 객체가 장식하고 있는 것을 위한 인스턴스 변수가 존재 (Whip, Mocha)

 

728x90
반응형
728x90
반응형

이 글은 안드로이드 공식 문서와 냉동 코딩님의 안드로이드 강의를 보고 작성하였다.

https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko 

 

ViewModel 개요  |  Android 개발자  |  Android Developers

ViewModel을 사용하면 수명 주기를 인식하는 방식으로 UI 데이터를 관리할 수 있습니다.

developer.android.com

 


ViewModel

" ViewModel 클래스는 비즈니스 로직 또는 화면 수준 상태 홀더입니다. UI에 상태를 노출하고 관련 비즈니스 로직을 캡슐화합니다. 주요 이점은 상태를 캐시하여 구성 변경에도 이를 유지한다는 것입니다. 즉, 활동 간에 이동하거나 구성 변경(예: 화면 회전 시)을 따를 때 UI가 데이터를 다시 가져올 필요가 없습니다. "    - 안드로이드 공식 문서 왈-

공식 문서는 참 말을 어렵게 하는 것 같다...  코드로 예시를 보면서 살펴보자.

 

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        var counter = 100
        binding.textView.text = counter.toString()

        binding.button.setOnClickListener {
            counter += 1
            binding.textView.text = counter.toString()
        }

    }
}

 

위 코드는 초기값(counter)을 100으로 설정하고 버튼을 누를 때 마다 1씩 증가하도록 만든 코드이다. 5번 눌러 105를 만들었고 스마트폰을 회전 시켜 가로 모드로 만들었더니 100으로 초기화 된다.

 

이유가 뭘까? 아래의 그림을 살펴보자

스마트폰을 회전시킬 때 앱의 생명주기이다. 액티비티가 처음 실행되었을 때 onCreate()에 진입하면서 100으로 초기화 되었고 버튼을 누르면서 1을 증가시켜 105로 만들었다. 

 

화면을 회전 시키게 되면 다시 onCreate()에 진입하면서 counter 값이 다시 초기화된 것이다. 

 

그러면 우리는 증가된 counter 값이 다시 초기화되지 않게끔 생명주기에 관계없이 값을 가지고 있어야 한다. 우리는 onSavedInstanceState()로 bundle 형태로 값을 저장하였다. 하지만 bundle은 50k로 적은 용량을 저장해야 하고 우리는 생명주기에 관계 없이 데이터를 보존하기 위해 ViewModel을 사용하게 된다.

 

즉, ViewModel에서 생명 주기와 상관없이 데이터를 저장하고 가져올 수 있게 된다는 것이다.

 


ViewModel 

 

ViewModel을 적용하여 코드를 작성해보자.

 

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        // 뷰모델 적용
        val myViewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 싱글턴 패턴
        myViewModel.counter = 100
        binding.textView.text = myViewModel.counter.toString()

        binding.button.setOnClickListener {
            myViewModel.counter += 1
            binding.textView.text = myViewModel.counter.toString()
        }

    }
}

 

MyViewModel.kt

class MyViewModel : ViewModel() {
    var counter : Int = 0
}

- MyViewModel 클래스를 생성하고 counter 값을 가져오도록 코드를 수정했다.

- 하지만 역시 onCreate()에서 myViewModel.counter = 100 에서 보듯 초기화는 onCreate()에서 발생하므로 여전히 회전 시 숫자가 100으로 돌아오는 문제점이 남아있다.

- ViewModelProvider에서 값을 초기화하는 것은 불가능하므로 팩토리 패턴을 사용하여 문제를 해결해보겠다.

 


 

팩토리 패턴 적용

 

MyViewModelFactoy.kt

class MyViewModelFactory(private val counter:Int):ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if(modelClass.isAssignableFrom(MyViewModel::class.java)){
            return MyViewModel(counter) as T
        }
        throw IllegalArgumentException("ViewModel class not found")
    }

}

- MyViewModelFactory를 클래스를 추가한다.

- 팩토리 패턴을 사용하는 이유는 ViewModelProvider 에서 counter 값을 초기화하지 못하기 때문이다.

- 팩토리 패턴에 대해서는 글 작성 후 링크 남기겠다.

 

MyViewModel.kt

class MyViewModel(_counter : Int) : ViewModel() {
    var counter = _counter
}

- 객체 생성 시 counter 값을 매개변수로 받을 수 있게 수정하였다.

 

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        val factory = MyViewModelFactory(100, this)
        val myViewModel : MyViewModel by viewModels { factory }
        binding.textView.text = myViewModel.counter.toString()

        binding.button.setOnClickListener {
            myViewModel.counter += 1
            myViewModel.saveState()
            binding.textView.text = myViewModel.counter.toString()
        }
    }
}

- 팩토리를 추가하면서 생명주기에 상관없이 counter 값을 유지할 수 있게 되었다!!!!

- 하지만 앱이 강제 종료가 된 경우 ViewModel에서 counter의 값이  유지되지 못하는 문제가 생긴다.

- SavedStateHandle 을 사용하여 문제를 해결해보자!

 


SavedStateHandle

 

MyViewModelFactoy.kt

class MyViewModelFactory(
    private val counter:Int,
    owner:SavedStateRegistryOwner,
    defaultArgs: Bundle? = null,
    ):AbstractSavedStateViewModelFactory(owner, defaultArgs){
    override fun <T : ViewModel> create(
        key: String,
        modelClass: Class<T>,
        handle: SavedStateHandle
    ): T {
        if(modelClass.isAssignableFrom(MyViewModel::class.java)){
            return MyViewModel(counter, handle) as T
        }
        throw IllegalArgumentException("Viewmodel class not found")
    }
    
}

 

MyViewModel.kt

class MyViewModel(
    _counter : Int,
    private val savedStateHandle:SavedStateHandle
) : ViewModel() {
    var counter = savedStateHandle.get<Int>(SAVED_STATE_KEY)?: _counter

    fun saveState(){
        savedStateHandle.set(SAVED_STATE_KEY, counter)
    }
    companion object{
        private const val SAVED_STATE_KEY = "counter"
    }
}

 

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

         //리셋되는 카운터
//        var counter = 100
//        binding.textView.text = counter.toString()
//
//        binding.button.setOnClickListener {
//            counter += 1
//            binding.textView.text = counter.toString()
//        }

//        val myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
//        myViewModel.counter = 100
//        binding.textView.text = myViewModel.counter.toString()
//
//        binding.button.setOnClickListener{
//            myViewModel.counter += 1
//            binding.textView.text = myViewModel.counter.toString()
//        }

        val factory = MyViewModelFactory(100,this)
//        val myViewModel = ViewModelProvider(this,factory).get(MyViewModel::class.java)
        // 위임
        val myViewModel by viewModels<MyViewModel>(){ factory}
        binding.textView.text = myViewModel.counter.toString()

        binding.button.setOnClickListener{
            myViewModel.counter += 1
            binding.textView.text = myViewModel.counter.toString()
            myViewModel.saveState()
        }
    }
}

- 앱 강제 종료 시에도 값이 유지됨을 확인할 수 있다.

 


정리

- 생명주기에 따라 변수의 값이 바뀔 수 있다. 우리는 생명주기에 상관없이 일정한 변수값을 가지기 위해 ViewModel을 사용한다.

- ViewModelProvider에서 변수값을 초기화할 수 없기 때문에 팩토리 패턴을 사용한다.

- 앱 강제 종료 시에도 값을 유지하고 싶으면 SaveStateBundle을 활용한다.

728x90
반응형
728x90
반응형

싱글턴 패턴에 대한 설명은 아래 링크를 참고하길 바란다.

https://codingjobrice.tistory.com/155

 

[디자인 패턴 기초] 싱글턴 패턴 (Singleton Pattern)

싱글턴 패턴이란? "클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공한다." 말 그대로 하나의 클래스로 하나의 객체만 만들겠다는 이야기다. 이해를 돕기위해 코드를 살펴보

codingjobrice.tistory.com

 

안드로이드에서 싱글턴 패턴 사용하기

안드로이드에서 단일 객체를 생성하기 위해서는 간단하게 object 키워드를 사용하면 된다.

object OneSingletonKt{
    var uniqueInstance:Int = 10
}

fun main(){
    println(OneSingletonKt.uniqueInstance)
}

 

uniqueInstance는 단일 객체가 된 것이다. 저렇게만 사용하면 간단하지만 이를 클래스 안에 담아보자.

class SingletonKt{
    object OneSingletonKt{
        var uniqueInstance:Int = 10
    }
}

fun main(){
    println(SingletonKt.OneSingletonKt.uniqueInstance)
}

이 것도 막 불편하지는 않지만 SingletonKt.OneSingletonKt.uniqueInstance 이 부분에서 보기 불편하다... 코틀린은 불편한 것 못참는 언어이다. 그래서 "companion" 이라는 키워드를 사용하면서 이 문제를 해결하였다. 코드를 살펴보자.

class SingletonKt{
    object OneSingletonKt{
        var uniqueInstance:Int = 10
    }
    
    companion object{
        var uniqueInstance2:Int = 20
    }
}

fun main(){
    println(SingletonKt.uniqueInstance2)
}

불편함을 해결한 것을 볼 수 있다.

 

그러면 안드로이드에서는 언제 사용될까? 

전역 변수, Retrofit 객체 생성 시, 데이터베이스 접근 시 등 다양한 경우에 사용되며 이는 하나하나 계속 추가해가겠다..

일단 firebase 객체 선언 시 사용한 코드를 예로 들겠다.

class MyApplication : MultiDexApplication(){
    companion object {
        lateinit var auth: FirebaseAuth
        var email: String? = null
      
        var user:User? = null
        lateinit var db: FirebaseFirestore
       
        fun checkAuth(): Boolean {
            var currentUser = auth.currentUser
            return currentUser?.let {
                email = currentUser.email
                currentUser.isEmailVerified
            } ?: let {
                false
            }
        }
    }
    override fun onCreate() {
        super.onCreate()
        auth = Firebase.auth
        db = FirebaseFirestore.getInstance()
    }
}

 

728x90
반응형

+ Recent posts