Java 프로그래밍에서의 단례 디자인 모델에 대해 간단히 이야기하다

7692 단어 Java단례
소프트웨어를 쓸 때 인쇄 로그 기능을 자주 사용해야 하기 때문에 디버깅과 포지셔닝 문제를 도와줄 수 있고 프로젝트가 출시된 후에 데이터 분석을 도와줄 수 있다.하지만 자바는 원래 가지고 있는 시스템입니다.out.println () 방법은 진정한 프로젝트 개발에서 거의 사용되지 않으며, 심지어findbugs 등 코드 검사 도구는 시스템을 사용한다고 생각할 수도 있다.out.println () 은 버그입니다.
왜 자바 초보 신기의 시스템으로out.println (), 진정한 프로젝트 개발에서 버림받을까요?사실 자세히 분석하기만 하면 너는 그것의 많은 폐단을 발견할 수 있다.예를 들어 제어할 수 없으며, 모든 로그는 프로젝트가 출시된 후에 평상시와 같이 인쇄되어 운행 효율을 떨어뜨린다.로컬 파일에 로그를 기록할 수도 없고, 인쇄가 지워지면 로그를 다시 찾을 수 없습니다.또는 인쇄된 내용이 Tag 구분이 없으면 이 로그가 어떤 종류에서 인쇄되었는지 판별하기 어려울 것입니다.
너의 리더도 바보가 아니야, 시스템으로.out.println()의 각종 폐단도 그가 똑똑히 알고 있기 때문에 그가 오늘 너에게 준 임무는 로그 도구 종류를 만들어서 더욱 좋은 로그 기능을 제공하는 것이다.그러나 당신의 리더는 괜찮습니다. 처음부터 모든 기능을 갖춘 우박 로그 도구 종류를 실현하지 못하게 했습니다. 인쇄 단계를 제어할 수 있는 로그 도구만 있으면 됩니다.
이 수요는 당신에게 결코 어렵지 않습니다. 당신은 즉시 작성하기 시작했고 곧 첫 번째 버전을 완성했습니다.

  public class LogUtil { 
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
  } 
이 클래스를 통해 로그를 출력합니다. level의 레벨만 제어하면 인쇄된 내용을 자유롭게 제어할 수 있습니다.예를 들어 현재 프로젝트가 개발 단계에 있으면 level을 DEBUG로 설정하면 모든 로그 정보가 인쇄됩니다.프로젝트가 출시되면 level을 INFO로 설정할 수 있습니다. 그러면 INFO와 이상의 로그 인쇄만 볼 수 있습니다.오류 로그만 보고 싶으면 레벨을 ERROR로 설정할 수 있습니다.만약 당신이 개발한 프로젝트가 클라이언트 버전이라면 로그를 출력하지 않으려면 level을 NOTHING으로 설정할 수 있습니다.인쇄할 때 호출만 하면 됩니다.

  new LogUtil().debug("Hello World!"); 
너는 지체하지 않고 이 도구를 너의 리더에게 소개했다. 너의 리더는 너의 소개를 듣고 나서 말했다. "좋아, 앞으로 모두들 네가 쓴 이 도구로 일지를 인쇄할 거야!"
그러나 얼마 지나지 않아 당신의 리더가 당신을 찾아 문제를 피드백했습니다.그는 비록 이 도구는 사용하기 쉽지만, 이런 일을 인쇄하는 것은 대상을 구분하지 않는다. 여기는 로그를 인쇄해야 할 때마다 new가 새로운 LogUtil을 만들어야 한다. 메모리를 너무 많이 차지하기 때문에 이 도구를 단례 모드로 바꾸길 바란다.
당신은 당신의 리더가 말한 것이 매우 일리가 있다고 생각합니다. 그리고 당신도 이 기회를 틈타 디자인 모델을 연습하고 싶어서 다음과 같은 코드를 썼습니다(ps: 여기 코드는 제가 직접 실현한 것입니다. 그리고 저는 라인 동기화 문제에 주의하지 않았습니다).

  public class LogUtil { 
    private static LogUtil logUtilInstance; 
   
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    private LogUtil() { 
   
    } 
   
    public static LogUtil getInstance() { 
      if (logUtilInstance == null) { 
        logUtilInstance = new LogUtil(); 
      } 
   
      return logUtilInstance; 
    } 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public static void main(String[] args) { 
      LogUtil.getInstance().debug("Hello World!"); 
    } 
  } 

우선 LogUtil의 구조 함수를 사유화하면 new 키워드를 사용하여 LogUtil의 실례를 만들 수 없습니다.그리고 sLogUtil 개인 정적 변수를 사용하여 실례를 저장하고 공유된 get Instance 방법을 제공하여 LogUtil의 실례를 가져오는 데 사용합니다. 이 방법에서 sLogUtil이 비어 있으면 new에서 새로운 LogUtil 실례를 제시합니다. 그렇지 않으면 sLogUtil로 바로 돌아갑니다.이렇게 하면 메모리에 LogUtil의 실례가 하나만 존재한다는 것을 보장할 수 있다.단일 모드 완공!이 때 로그를 인쇄하는 코드는 다음과 같이 변경해야 합니다.

  LogUtil.getInstance().debug("Hello World");  

이 버전을 리더에게 보여주자 그는 웃으며 "단례 모드를 구현한 것 같지만 버그가 존재한다"고 말했다.
너는 의심이 가득한데, 단일 패턴은 모두 이렇게 실현된 것이 아니냐?또 무슨 버그가 있을까요?
당신의 리더는 단일 모드를 사용하는 것은 이 종류가 메모리에 하나의 실례만 있을 수 있도록 하기 위해서입니다. 그러나 다중 노드에서 로그를 출력하는 상황을 고려하고 있습니까?다음 코드와 같습니다.

  public static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      logUtilInstance = new LogUtil(); 
    } 
   
    return logUtilInstance; 
  } 

만약에 현재 두 개의 라인이 동시에 get Instance 방법을 실행하고 있다면 첫 번째 라인은 두 번째 라인을 실행하고 세 번째 라인을 실행하지 않았습니다. 이때 두 번째 라인은 두 번째 라인에 실행되었습니다. 이것은 sLogUtil인지null인지를 발견하고if판단에 들어갔습니다.이렇게 하면 당신의 단일 모드는 실패합니다. 왜냐하면 두 개의 다른 실례를 만들었기 때문입니다.
너는 문득 크게 깨달았지만 너의 사고방식은 매우 빠르다. 즉각 해결 방법을 생각해냈다. 방법에 동기화 자물쇠를 추가하면 된다. 코드는 다음과 같다.

  public synchronized static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      logUtilInstance = new LogUtil(); 
    } 
   
    return logUtilInstance; 
  } 

이렇게 하면 같은 시간에 get Instance 안의 코드를 실행하는 라인만 허용됩니다. 그러면 위에서 두 개의 실례를 만들 수 있는 상황을 효과적으로 해결할 수 있습니다.
당신의 리더는 당신의 새 코드를 보고 말했다. "응, 괜찮아. 두 개의 실례를 만들 수 있는 상황을 해결했지만, 이 코드는 문제가 있어."
너는 긴장하기 시작했는데, 어째서 또 문제가 있겠니?
당신의 리더는 웃습니다."긴장하지 마세요. 이번에는 bug가 아니라 성능적으로 최적화할 수 있어요. 한번 보세요. get Instance 방법에synchronized가 추가되면 저는 get Instace 방법을 실행할 때마다 동기화 자물쇠의 영향을 받아요. 이렇게 하면 실행 효율이 낮아집니다. 사실 Log Util 실례를 처음 만들 때 동기화 자물쇠를 추가하면 돼요. 제가 어떻게 그것을 최적화하는지 가르쳐 드릴게요."
먼저 synchronized 키워드를 방법 설명에서 제거하고 방법체에 추가합니다.

  public static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      synchronized (LogUtil.class) { 
        if (logUtilInstance == null) { 
          //  , synchronized  
          logUtilInstance = new LogUtil(); 
        } 
      } 
    } 
   
    return logUtilInstance; 
  } 

코드가 이렇게 바뀌면 sLogUtil이 초기화되지 않았을 때만 세 번째 줄에 들어가고 동기화 자물쇠를 추가합니다.sLogUtil이 초기화되면 더 이상 세 번째 줄에 갈 수 없습니다. 이렇게 하면 get Instance 방법을 실행해도 동기화 자물쇠의 영향을 받지 않고 효율이 어느 정도 향상될 것입니다.
너는 이 방법이 정말 교묘하구나, 생각해 낼 수 있다는 것은 정말 너무 총명하다는 것을 절로 감탄하게 되었다.
너의 리더는 즉시 겸손해졌다. "이런 방법은 이중 잠금(Double-Check Locking)이라고 하는데, 내가 생각한 것이 아니다. 더 많은 자료를 인터넷에서 찾아볼 수 있다."
사실 자바에서 예를 들면 제가 배고픈 모드를 쓰는 게 더 익숙해요.
게으름뱅이식의 특징은 로딩을 늦추는 것이다. 실례는 사용할 때까지 로딩할 수 있다
굶주림식의 특징은 처음부터 불러오기 때문에 매번 사용할 때마다 바로 되돌아오면 된다는 것이다(이것을 더욱 추천합니다. 왜냐하면 너무 많은 스레드 안전 문제를 고려할 필요가 없기 때문입니다. 물론 게으름뱅이는 위에서 말한 이중 잠금을 통해 동기화 문제를 해결할 수 있습니다.)
로그 기록을 기아식으로 실현하는 코드는 다음과 같다.

  public class LogUtil { 
    private static final LogUtil logUtilInstance = new LogUtil(); 
   
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    private LogUtil() { 
   
    } 
   
    public static LogUtil getInstance() { 
      return logUtilInstance; 
    } 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public static void main(String[] args) { 
      logUtil.getInstance().debug("Hello World!"); 
    } 
  } 

좋은 웹페이지 즐겨찾기