반응형

 

싱글톤 패턴은 소프트웨어 디자인 패턴중 하나이다.

 

주로 사용되는 경우는 단 하나만 존재해야되는 물건이 있을 때!

프로그램 내부에서 단 1개만 생성됨을 보장하며 멀티쓰레드에서 이 객체를 동시에 접근하는 경우에 발생하는 동시성 문제를 해결해준다.

 

말로설명하면 어렵고 코드를 보면 쉽다.

class Order {
    public void start() {
        A a = A.getInstance();
        A b = A.getInstance();
        // 확인
        System.out.println(a == b); // true
    }
}

 

만약 이렇게 있다고 치면, a 와 b 는 같은 객체를 보고있게 된다.

이런 로직을 만들 필요가 있을때 싱글톤패턴을 사용한다.

 


1. 기본적인 구현방법 (리소스낭비)

public class A {

	private A() {
           System.out.println("Hello singleton!!");
	}

	private static A singleton = new A(); //객체생성

	public static A getInstance() {
            return A;
	}

}

 

private 생성자를 사용하여 외부에서의 생성을 막은것이 포인트다

단점으로는 클래스가 로드될때 객체가 생성되므로 리소스 낭비가 있을 수 있다.

 

그렇다면 어떻게 개선할수있을까?

 

2. 기본적인 구현방법 (리소스낭비 줄임)

public class A {
    // 단 1개만 존재해야 하므로 static
    private static A a;

    // private로 외부생성을 막음
    private A() {
       System.out.println("Hello singleton!!");
    }

    //외부에서는 getInstance로 접근
    public static A getInstance() {
      if (instance == null){
          instance = new A();
      }
      return instance; 
    } 
}

 

1) getInstance()

 - static을 쓰지않으면 외부에서 사용할때

new A().new getInstance()

이렇게 새로운 인스턴스를 선언하면서 써야하기때문에 싱글톤이 보장되지 않는다.

따라서 static을 사용하고 if 문을 통하여 기존에 불려진 instance가 없다면 생성, 있다면 기존 instance를 return

 

하지만 단점!

이 경우 Multi Thread 환경에서 Thread-safe 를 보장해주지 않는다.

n개의 사용자가 동시에 getInstance를 했을때 new A() 를 n번 생성할수도있다.

 

이를 막기위해서 Synchronized, Double check 등을 사용해서 막아볼수도 있겠지만, 가장많이 쓰이는 방법은 아래와 같다.

 

3. LazyHolder 싱글톤 패턴 ✔️

public class A {

    private A() {
       System.out.println("Hello singleton!!");
    }
    
    private static class LazyHolder() {
        private static final A instance = new A();
    }

    public static A getInstance() {
        return LazyHolder.instance;
    }
    
}

 

 

훨씬 코드가 예뻐진것을 볼수있다.

 

해당 클래스가 로드되더라도 getnstance() 메서드가 호출될때까지 로드되지 않으므로 리소스 낭비도 줄이고

 

A.getInstance() 를 호출할때마다 동일한 instance가 변환된다. 왜냐! private static final 로 선언했기때문!

 

사용은 아래와 같이 쓰면된다

public static void main(String[] args) {
	A a = A.getInstance();
}

Spring 의 경우에는 static 기반의 싱글톤 구현을 지양하고있으며 더욱 간단하게 쓸수있다.

 

Spring - @Autowired 사용

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {
    public A() {
        System.out.println("Hello singleton!!");
    }
}

@Component
public class Main {
    @Autowired
    private A a; // A의 싱글톤 인스턴스가 주입됨

    public void display() {
        System.out.println(a);
    }
}

// 메인 메서드
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Main main = context.getBean(Main.class);
        main.display();

        // 다시 가져와서 같은 인스턴스인지 확인
        Main anotherMain = context.getBean(Main.class);
        System.out.println(main.a == anotherMain.a); // true, 같은 인스턴스
    }
}

 

위와 같이 인스턴스 필드로 의존성 주입을 사용하는 경우, 싱글톤 패턴이 유지된다.

Spring 컨테이너는 @Component, @Service, @Repository, @Controller 등으로 정의된 클래스의 인스턴스를 싱글톤으로  관리한다

 

항상 어노테이션을 사용하여 써왔기때문에 싱글톤 패턴을 정석으로 봤을때 생소했던것같다.

728x90

'CS' 카테고리의 다른 글

종속성, 의존성  (0) 2024.10.31
반응형

 

항상 사용하고있던 종속성, 의존성에 대한 고찰.

 

종속성

: 한 클래스가 다른 클래스나 기능을 필요로 할때 발생하는 관계.

예를들어 class B 가 class A 의 변수 혹은 함수에 의존한다면, class B는 class A의 종속성을 가진다고 할 수 있다.

 

의존성

: 종속성을 관리하는 방식.

흔히 알고있는 DI (Dependency Injection) 의존성 주입 이라고한다.

 

예를들어 class B에서 class A를 선언하지않고, 외부에서 class A의 인스턴스를 주입받으면 class A의 속성이나 인스턴스 그대로 사용가능하다.

 

이것을 왜 헷깔리고 자주 잊는가?

나는 개인적으로 외부 class를 선언하고 사용하는것에 너무 익숙해져있기 때문이다.

 

그러한 행위가 가능한 이유는 전부 DI 덕분이라는것을 다시 생각해보았다. 

728x90

'CS' 카테고리의 다른 글

싱글톤 패턴의 이해와 응용  (0) 2024.10.31
반응형

작업은 vscode 에서 진행한다.

만약 없다면 무료 툴 이므로 다운로드!

 

1. node js 설치

https://nodejs.org/en

 

 

2.  cordova 설치

cmd 창에서 진행하면 된다

$ npm install -g cordova

 

3. cordova 프로젝트 생성

내가원하는 폴더로 이동 후 아래 커맨드 실행

$ cordova create [프로젝트명] [패키지명] [프로젝트명]

ex)
$ cordova create cordova1 com.example.test cordova1

 

이렇게 프로젝트가 생성된다

 

처음에는 기본적으로 www 즉 웹만 생성되며 실행시켜볼수도있다.

 

vscode 플러그인으로 live server 를 추가하고 (구글링) 실행하면 코르도바 이미지가 보인다

 

 

 

4.  Android, iOS platform 추가

아래 커맨드로 안드로이드 소스코드를 추가할 수 있다

$ cordova platform add android

 

마찬가지로 ios로 추가해주면 아래와같이 platoforms에 android 와 ios가 생긴다

 

Android 같은 경우는, java 기반으로 소스코드가 생기는것을 확인해 볼 수 있다.

 

5. project build

 

이제 이것을 vscode에서 그대로 build 하려면 ios의 경우는 맥북이 필요하고

android 는 build 가능하나, 필요한 requirements들이 있다 (jdk 등등) 대신 안드로이드 스튜디오에서 해당프로젝트 실행해도됨

 

$ cordova requirements

 

나의 경우 위에서부터 하나씩 보면 

jdk : 17버전 있음

sdk  : 있음

android target : 없음

gradle : 없음

macOS : 없음

 

이렇게 확인된다

 

특히 android target 부분이나 밑에 gradle 을 보면 please install android studio 라는 부분을 볼수있는데 안드로이드 스튜디오에서 해당 프로젝트를 빌드하는게 속편할것같다

728x90
반응형

 

Example 4 : s = "{()}" output = true;

 

예시를 보면 소/중/대괄호가 짝지어서 나오고 Example 4 와 같이 속에있을수도있다.

 

처음에는 String 의 Char.At(i) 가 ( 혹은 { 혹은 [ 일때 Char.At(i +1) 이 ) 혹은 } 혹은 ] 인것을 확인했는데

example 4 때문에 그건 불가능했다.

 

해당문제는 여는 괄호가 나오면 그다음엔 무조건 닫는 괄호가 나와야하기때문에 여는 괄호가 나오면 stack 에 저장해두고, 더이상 여는 괄호가 없으면 stack 에서 가장 나중에 저장된 괄호를 지우는 방식으로 진행했다.

 

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
    }
}

 

여는괄호인지를 알기위해서 map을 만들어줬다.

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        
        for (int i=0; i<s.length(); i++) {
        	char c = s.charAt(i);
            
            if (map.containsKey(c)) { //여는 괄호가 나왔다면
            	stack.push(c);
            } else {
            	if (c != map.get(stack.pop()) {
                   return false;
                }
            }
        }
        
        return stack.length == 0;
    }
}

 

 여는 괄호가 나왔다면 stack에 넣어주고 

닫는괄호가 나왔다면, stack에 있는 여는괄호를 불러서 map.get 으로 나온 value와 비교하였다.

 

그리고 모든 닫는괄호가 정상적으로 짝지어나왔다면 stack.length 는 0일 것이므로 return 해줬다.

 

그런데 이렇게 하면 s="]" 일때 .. stack 에도 값이 없기때문에 else 로 빠지지만, 갈곳이없다..!

 

때문에 empty 도 같이 봐준다.

 

class Solution {
    public boolean isValid(String s) {
    	Stack<Character> stack = new Stack<>();
        Map<Character, Character> map = new HashMap<>();
        map.put('(',')');
        map.put('{','}');
        map.put('[',']');
        
        for (int i=0; i<s.length(); i++) {
        	char c = s.charAt(i);
            
            if (map.containsKey(c)) { //여는 괄호가 나왔다면
            	stack.push(c);
            } else {
            	if (stack.isEmpty() || c != map.get(stack.pop())) {
                   return false;
                }
            }
        }
        
        return stack.size() == 0;
    }
}

 

 

 

728x90
반응형

 

 

딱 봤을때 String 으로 만들어서 뒤집어야겠다는 생각을 했다.

 

import java.util.*;

class Solution {
    public long solution(long n) {
        
        //String 배열로 만듦
        String[] arr = String.valueOf(n).split("");
        
        //순서 뒤집음
        Arrays.sort(arr, Comparator.reverseOrder());
        
        //하나의 String으로 변환
        String str = "";
        for (String s : arr) {
            str += String.valueOf(s);
        }
        
        //long 변환
        long answer = Long.parseLong(str);
        return answer;
    }
}

 

그런데 String 으로 변환하는 부분이 마음에 들지않는다.

좀더 간단한 방법이 있을것같고, 내림차순으로 하기위해서 Coparator.reverseOrder() 라는 함수가 있다는것을 외워야한다.

 

다른 사람의 풀이를 살펴보았다.

 

import java.util.*;

class Solution {
    public long solution(long n) {
        
        //String 배열로 만듦
        String[] arr = String.valueOf(n).split("");
        
        //순서 뒤집음
        Arrays.sort(arr);
        
        //하나의 String으로 변환
        StringBuilder sb = new StringBuilder();
        for (String s : arr) sb.append(s);
        
        //long 변환
        long answer = Long.parseLong(sb.reverse().toString());
        return answer;
    }
}

 

StringBuilder 를 사용하면 Comparator.reverseOrder() 이라는 다소 긴 메소드를 기억할 필요없이

new StringBuilder 선언하고 append 로 담은다음에 sb.reverse() 만 해주면 된다.

 

728x90

+ Recent posts