Alibaba 백엔드 인턴십 경험

Alibaba 백엔드 인턴십 경험

es가 프로젝트에 사용되는데, es의 역할은 무엇인가요?

Elasticsearch는 대규모 데이터에서 필요한 콘텐츠를 빠르게 찾는 데 도움이 되는 많은 강력한 기능을 갖춘 매우 강력한 오픈 소스 검색 엔진입니다.

 

es의 중요한 개념은 무엇입니까?

클러스터: 전체 데이터를 함께 보관하고 모든 노드에 걸쳐 통합 색인화 및 검색 기능을 제공하는 하나 이상의 노드(서버) 모음입니다. 클러스터는 기본적으로 "elasticsearch"인 고유 이름으로 식별됩니다. 노드는 이름으로 클러스터에 참여하도록 설정된 경우에만 클러스터의 일부가 될 수 있으므로 이 이름이 중요합니다.

 

노드: 클러스터의 일부인 단일 서버입니다. 데이터를 저장하고 클러스터 인덱싱 및 검색 기능에 참여합니다.

 

색인: 관계형 데이터베이스의 "데이터베이스"와 같습니다. 여러 유형을 정의하는 맵이 있습니다. 인덱스는 하나 이상의 기본 샤드에 매핑되는 논리적 네임스페이스이며 0개 이상의 복제본 샤드를 가질 수 있습니다.

예: MySQL =>데이터베이스 ElasticSearch =>색인

 

문서: 관계형 데이터베이스의 행과 유사합니다. 차이점은 인덱스의 각 문서가 서로 다른 구조(필드)를 가질 수 있지만 공통 필드의 데이터 유형은 동일해야 한다는 것입니다.

MySQL => 데이터베이스 => 테이블 => 열/행 ElasticSearch => 인덱스 => 유형 => 속성이 있는 문서

 

유형: 인덱스의 논리적 범주/파티션이며 의미는 전적으로 사용자에게 달려 있습니다.

 

 

es가 빠른 이유와 mysql에 비해 장점은 무엇입니까?

데이터베이스에 쿼리할 때 ID로 쿼리하면 바로 인덱스로 들어가는데, 쿼리 속도가 매우 빠르다. 하지만 제목을 기준으로 퍼지 쿼리를 수행하면 데이터를 한 줄씩만 검색할 수 있으며 프로세스는 다음과 같습니다.

  1. 사용자가 데이터를 검색하고 제목이 일치하는 조건 "%手机%"
  2. ID가 1인 데이터와 같이 행별로 데이터를 가져옵니다.
  3. 데이터의 제목이 사용자의 검색 조건을 충족하는지 확인
  4. 일치하면 결과 집합에 포함되고, 일치하지 않으면 삭제됩니다. 1단계로 돌아가세요.

이 전체 테이블 스캔 방법은 데이터 양이 많을 때 많은 시간을 소비하게 됩니다. 이러한 문제를 해결하기 위해 전기탐색에서는 역지수법을 사용한다.

먼저 두 가지 개념을 소개합니다.

  • 문서: 검색에 사용되는 데이터로, 각 데이터가 문서입니다. 예를 들어 웹페이지나 제품 정보 등이 있습니다.
  • 용어(Term): 특정 알고리즘을 이용하여 문서 데이터나 사용자 검색 데이터를 분할하고, 획득한 의미가 있는 단어를 용어라고 합니다. 예를 들어 I am Chinese는 I, am, Chinese, China, Chinese 등 여러 용어로 나눌 수 있습니다.

 

역방향 인덱스를 생성하는 것은 순방향 인덱스의 특수 처리로, 그 과정은 다음과 같습니다.

  • 각 문서의 데이터는 각 항목을 얻기 위한 알고리즘을 사용하여 단어로 분할됩니다.
  • 테이블을 생성하면 각 데이터 행에는 용어, 문서 ID, 용어가 있는 위치 등의 정보가 포함됩니다.
  • 용어의 고유성으로 인해 해시 테이블 구조 인덱스와 같은 용어에 대한 인덱스가 생성될 수 있습니다.

 

c0a0db9736067f3896b6f8ac058a9bd6.png

반전된 인덱스의 검색 프로세스 는 다음과 같습니다(예: "Huawei 휴대폰" 검색).

1) 사용자가  "华为手机" 검색할 기준을 입력합니다.

2) 사용자 입력 내용을 단어로 분할 하고 항목을 얻습니다: 华为, 手机.

3) 항목이 포함된 반전된 색인을 검색하면 해당 항목이 포함된 문서 ID(1, 2, 3)를 얻을 수 있습니다.

4) 문서 ID를 가져와 정방향 색인에서 특정 문서를 찾습니다.

 

0ad85ba090b0295f92ed7ab65b141e63.png

역색인을 먼저 조회한 후 역색인을 조회해야 하는데, 용어와 문서ID가 모두 색인되어 있어서 조회 속도가 매우 빠릅니다! 전체 테이블 스캔이 필요하지 않습니다.

  • 장점: 용어 기반 검색, 퍼지 검색 시 속도가 매우 빠르다.
  • 단점: 인덱스는 필드가 아닌 용어에 대해서만 생성할 수 있으며 필드를 기준으로 정렬할 수 없습니다.

 

프로젝트에 트랜잭션이 사용됩니까? 사업에 대해 이야기해보자

데이터베이스 트랜잭션은 다양한 데이터 항목에 액세스하고 이를 운영하는 일련 의 데이터베이스 작업 입니다 . 이러한 작업은 모두 실행되거나 전혀 실행되지 않습니다. 이들은 분할할 수 없는 작업 단위입니다. 트랜잭션은 트랜잭션의 시작과 끝 사이에 수행되는 모든 데이터베이스 작업으로 구성됩니다.

거래의 특성(ACID)

- 원자성: 트랜잭션은 더 이상 나눌 수 없는 애플리케이션에서 가장 작은 실행 가능 본체입니다.

- 일관성: 트랜잭션 실행 결과에 따라 데이터가 한 일관성 상태에서 다른 일관성 상태로 변경되어야 합니다.

- 격리: 각 트랜잭션의 실행은 서로 간섭하지 않으며 모든 트랜잭션의 내부 작업은 다른 트랜잭션과 격리됩니다.

- 내구성: 트랜잭션이 커밋되면 데이터에 대한 모든 변경 사항이 영구 저장소에 기록되어야 합니다.

일반적인 동시성 예외

업데이트 손실의 첫 번째 유형: 트랜잭션 롤백으로 인해 다른 트랜잭션에 의해 업데이트된 데이터가 손실됩니다.

두 번째 유형의 업데이트 손실: 특정 트랜잭션을 제출하면 다른 트랜잭션에서 업데이트한 데이터가 손실됩니다.

 

더티 읽기(Dirty Read): 트랜잭션이 다른 트랜잭션에서 커밋되지 않은 데이터를 읽습니다.

반복 불가능 읽기: 특정 트랜잭션에서 동일한 데이터에 대한 읽기 전후의 읽기 결과가 일치하지 않습니다.

팬텀 읽기: 특정 트랜잭션에서 동일한 테이블 전후에 쿼리되는 행 수가 일치하지 않습니다.

일반적인 격리 수준

커밋되지 않은 읽기: 커밋되지 않은 데이터를 읽습니다.

커밋된 읽기: 제출된 데이터를 읽는다는 것은 커밋된 콘텐츠만 읽을 수 있다는 의미입니다.

반복 읽기: 반복 읽기. 커밋된 읽기를 기준으로 잠금 세분성이 증가하고 트랜잭션 작업 중에 다른 항목의 업데이트가 허용되지 않으므로 더티 읽기(dirty read) 및 반복 불가능 읽기 문제는 해결되지만 팬텀 읽기(phantom read) 문제는 해결할 수 없습니다.

직렬화 가능: 직렬화, 트랜잭션은 "직렬화되어 순차적으로 실행됩니다". 즉, 대기열에 추가되어 하나씩 실행됩니다.

 

다양한 데이터베이스의 기본 격리 수준

MySQL:

MySQL의 기본 격리 수준은 "Repeatable Read" 입니다 . 즉, 동일한 트랜잭션 내에서 동일한 데이터를 여러 번 읽으면 동일한 결과가 제공되며 다른 트랜잭션은 트랜잭션 중에 데이터를 수정할 수 없습니다.

신탁:

Oracle의 기본 격리 수준은 "Read Committed" 입니다 . "Read Committed" 격리 수준에서 트랜잭션은 커밋된 데이터만 볼 수 있습니다. 이는 Oracle의 기본 동작이지만 Oracle은 "직렬화 가능"(직렬화 가능) 등과 같은 다른 격리 수준도 제공합니다.

 

 

b159043ad45ee0638d64151c98d54067.png

잠그다

• 비관적 잠금(데이터베이스)

공유 잠금(S 잠금) 트랜잭션 A가 특정 데이터에 공유 잠금을 추가한 후 다른 트랜잭션은 해당 데이터에 공유 잠금을 추가할 수만 있고 배타적 잠금은 추가할 수 없습니다.

배타적 잠금(X 잠금) 트랜잭션 A가 특정 데이터에 배타적 잠금을 추가한 후에는 다른 트랜잭션이 해당 데이터에 공유 잠금이나 배타적 잠금을 추가할 수 없습니다.

• 낙관적 잠금(사용자 정의)

데이터를 업데이트하기 전에 버전 번호가 변경되었는지 확인하세요. 변경된 경우 이 업데이트를 취소하고, 그렇지 않으면 데이터(버전 번호 + 1)를 업데이트하세요.

 

봄에 트랜잭션 사용하기

선언적 트랜잭션: 주석을 통해 메서드의 트랜잭션 특성을 선언합니다.

서비스 클래스에 @Transactional Annotation을 추가하는데, 이 Annotation에서는 트랜잭션 관련 파라미터를 설정할 수 있다.

    // REQUIRED: 현재 트랜잭션(외부 트랜잭션)을 지원하고, 존재하지 않으면 새 트랜잭션을 생성합니다.required 
    // REQUIRES_NEW: 새 트랜잭션을 생성하고 현재 트랜잭션(외부 트랜잭션)을 일시 중지합니다.requires_new 
    // NESTED: 만약 현재 트랜잭션이 존재하는 경우(외부 트랜잭션) 트랜잭션) 트랜잭션에 중첩되어 실행되며(독립적 커밋 및 롤백), 그렇지 않으면 REQUIRED와 동일합니다. 
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) 
    public Object save1() { 
        // 신규 사용자 추가 
        User user = new User(); 
        user.setUsername("alpha"); 
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5)); 
        user.setPassword(CommunityUtil .md5("123" + 사용자 .getSalt())); 
        user.setEmail("[email protected]"); 
        user.setHeaderUrl("http://image.nowcoder.com/head/99t.png") ; 
        user.setCreateTime(new Date( )); 
        userMapper.insertUser(user); 
 
        // 새 게시물 
        DiscussPost post = new DiscussPost(); 
        post.setUserId(user.getId()); 
        post.setTitle("Hello"); 
        post.setContent("New Report !"); 
        post.setCreateTime(new Date()); 
        DiscussPostMapper.insertDiscussPost(post); 
 
        //이 단계에서는 오류를 보고하고 롤백되었는지 관찰합니다 
        .Integer.valueOf("abc"); 
 
        "확인"을 반환합니다. 
    }

프로그래밍 방식 트랜잭션: TransactionTemplate을 통해 트랜잭션을 관리하고 이를 통해 데이터베이스 작업을 수행합니다.

    공용 개체 save2() { 
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); 
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 
 
        return transactionTemplate.execute(new TransactionCallback<Object>() { 
            @Override 
            public Object doInTransaction(TransactionStatus status) { 
                // 
                신규 사용자 user = new User(); 
                user.setUsername("beta"); 
                user.setSalt(CommunityUtil .generateUUID().substring(0, 5)); 
                user.setPassword(CommunityUtil.md5("123" + user.getSalt())); 
                user.setEmail("************") ; 
                user.setHeaderUrl("http://image.nowcoder.com/head/999t.png"); 
                user.setCreateTime(new Date()); 
                userMapper.insertUser(user); 
 
                // 新增帖子
                DiscussPost post = new DiscussPost(); 
                post.setUserId(user.getId()); 
                post.setTitle("你好"); 
                post.setContent("我是新人!"); 
                post.setCreateTime(new Date()); 
                DiscussPostMapper.insertDiscussPost (게시물); 
 
                //这步报错,观测是否回滚
                Integer.valueOf("abc"); 
 
                return "ok"; 
            } 
        });

 

Java 스레드 풀 및 관련 매개변수에 대해 이야기해 볼까요?

관련 프로세스:

 

bc4848f5d5f4eaca79bca2f87fde26da.png

스레드 풀에는 주로 다음 6개의 매개변수가 있습니다.

  1. corePoolSize(코어 작업자 스레드 수): 스레드 풀에 작업을 제출할 때 스레드 풀에서 생성된 스레드 수가 corePoolSize보다 적으면 이때 유휴 스레드가 있더라도 작업을 실행합니다. 새로운 스레드가 생성될 때까지 스레드 개수가 corePoolSize보다 크거나 같은 경우.
  2. maximumPoolSize(최대 스레드 수): 스레드 풀에서 허용하는 최대 스레드 수입니다. 대기열이 가득 차고 생성된 스레드 수가 maximumPoolSize보다 적으면 스레드 풀은 작업을 수행하기 위해 새 스레드를 생성합니다. 또한 제한되지 않은 대기열의 경우 이 매개변수를 무시할 수 있습니다.
  3. keepAliveTime(초과 스레드 생존 시간): 스레드 풀의 스레드 수가 코어 스레드 수보다 클 때 스레드의 유휴 시간이 스레드 생존 시간을 초과하면 스레드는 스레드 수만큼 소멸됩니다. 스레드 풀의 코어 스레드 수보다 작거나 같습니다.
  4. workQueue(작업 대기열): 실행 대기 중인 작업을 전송하고 저장하는 데 사용되는 차단 대기열입니다.
  5. threadFactory(스레드 생성 팩토리): 새 스레드를 생성하는 데 사용됩니다. threadFactory에 의해 생성된 스레드도 새로운 Thread() 메소드를 사용합니다.threadFactory에 의해 생성된 스레드 이름은 pool-m-thread-n이라는 통일된 스타일을 갖습니다.(m은 스레드 풀의 수, n은 스레드 풀의 수입니다. 스레드 풀).
  6. 핸들러(거부 정책): 스레드 풀과 큐가 가득 차면 스레드를 추가할 때 이 정책이 실행됩니다.

 

네 가지 거절 전략

  1. AbortPolicy: 작업을 취소하고 RejectedExecutionException을 발생시킵니다.
  2. DiscardPolicy: 작업도 삭제하지만 예외를 발생시키지는 않습니다.
  3. DiscardOldestPolicy: 대기열의 맨 앞에 있는 작업을 취소하고 작업 실행을 다시 시도합니다(프로세스 반복).
  4. CallerRunsPolicy: 거부된 실행 메서드는 호출 스레드에 의해 처리됩니다.

 

스레드 풀을 만드는 방법은 무엇입니까?

Executors 관련 함수를 사용하여 생성

1.newFixedThreadPool: 고정 크기 스레드 풀 생성

public class ThreadPool1 { 
    public static void main(String[] args) { 
        //1. 크기 5의 스레드 풀 생성 
        ExecutorService threadPool= Executors.newFixedThreadPool(5); 
        //2. 스레드 풀을 사용하여 
        (int에 대한 작업 실행) i=0;i<5;i++){ 
            //스레드 풀에 작업 추가 
            threadPool.submit(new Runnable() { 
                @Override 
                public void run() { 
                    System.out.println("스레드 이름"+Thread.currentThread ( ).getName()+"Executing task 1"); 
                } 
            }); 
        } 
        //2. 스레드 풀을 사용하여 작업 2를 실행합니다. 
        for (int i=0;i<8;i++){ 
            //작업 추가 스레드 풀 
            threadPool.submit(new Runnable() { 
                @Override 
                public void run() { 
                    System.out.println("스레드 이름"+Thread.currentThread().getName()+"Executing task 2"); 
                } 
            } ); 
        } 

    } 
}

2.newCachedThreadPool: 캐시가 있는 스레드 풀은 짧은 시간 내에 많은 수의 작업이 있는 시나리오에 적합하지만 더 많은 리소스를 차지할 수 있습니다. 스레드 수는 작업 양에 따라 다릅니다.

public class ThreadPool3 { 
    public static void main(String[] args) { 
        //스레드 풀 생성 
        ExecutorService service= Executors.newCachedThreadPool(); // 
        (int i=0;i<50;i++)에 대한 
        50개의 작업이 있습니다 . 
            int finalI = i; 
            service.submit(()->{ 
                System.out.println(finalI +"thread name"+Thread.currentThread().getName());//스레드 이름이 몇 개인지 CPU는 스레드 생성 
            }); 
        } 
    } 
}

3.newSingleThreadExecuto: 단일 스레드에 대한 스레드 풀 생성

공용 클래스 ThreadPool4 { 
    공용 정적 void main(String[] args) { 
        ExecutorService 서비스= Executors.newSingleThreadExecutor(); 
        for (int i=0;i<5;i++){ 
            int finalI = i; 
            service.submit(()- >{ 
                System.out.println(finalI +"thread name"+Thread.currentThread().getName());//CPU는 스레드를 1개만 생성하고 이름은 항상 동일합니다.} 
            ); 
        } 
    } 
}

4.newSingleThreadScheduledExecutor: 예약된 작업을 실행하는 단일 스레드에 대한 스레드 풀 생성

public class ThreadPool5 { 
    public static void main(String[] args) { 
        ScheduledExecutorService service= Executors.newSingleThreadScheduledExecutor(); 
        System.out.println("작업 추가: "+ LocalDateTime.now()); 
        service.schedule(new Runnable( ) { 

                @Override 
                public void run() { 
                    System.out.println("작업 실행: "+LocalDateTime.now()); 
                } 
            },3,TimeUnit.SECONDS);//3초 동안 작업 실행 지연 
    } 
}

 

스레드로컬에 대해 이야기해보자

ThreadLocal은 스레드의 지역 변수로 해석될 수 있는데, 즉 ThreadLocal 변수는 현재 자신의 스레드에서만 접근할 수 있고 다른 스레드에서는 접근할 수 없으므로 스레드 경쟁은 자연스럽게 회피된다.

 

사용:

ThreadLocal 개체를 만듭니다.

private ThreadLocal<Integer> localInt = new ThreadLocal<>();

위의 코드는 localInt 변수를 생성하는데, ThreadLocal은 일반 클래스이므로 여기서는 localInt 유형을 정수로 지정합니다.

이 변수의 값을 설정하고 가져오는 방법은 다음과 같습니다.

공개 int setAndGet(){ 
    localInt.set(8); 
    localInt.get()을 반환합니다. 
}

위 코드는 변수 값을 8로 설정한 다음 이 값을 가져옵니다.

ThreadLocal에 설정된 값은 현재 스레드 자체에만 표시되므로 다른 스레드를 통해 해당 값을 초기화할 수 없습니다. 이를 보완하기 위해 ThreadLocal은 모든 스레드의 ThreadLocal 값을 균일하게 초기화하는 withInitial() 메서드를 제공합니다.

private ThreadLocal<Integer> localInt = ThreadLocal.withInitial(() -> 6);

위 코드는 ThreadLocal의 초기 값을 모든 스레드에 표시되는 6으로 설정합니다.

 

해시맵의 최하위 레이어는 어떻게 구현되나요?

해시맵 정의:

HashMap은 배열 + 단일 연결 리스트 + 레드-블랙 트리로 구현된 맵 클래스입니다. 동시에 해당 어레이의 기본 초기 용량은 16이고 확장 계수는 0.75이며 확장은 매번 2배입니다.

HashMap은 Map 인터페이스를 구현하고, 키의 HashCode 값에 따라 데이터를 저장하며, 액세스 속도가 매우 빠르고, 한 레코드의 키가 최대 null일 수 있도록 허용하며, 스레드 동기화를 지원하지 않습니다.

HashMap은 순서가 없습니다. 즉, 삽입 순서가 기록되지 않습니다.

 

HashMap 저장 프로시저:

HashMap은 키에 따라 저장될 값의 해당 배열 첨자를 계산하며 해당 배열 첨자 위치에 요소가 없으면 저장된 요소가 저장됩니다. 그러나 해당 위치에 이미 요소가 있는 경우에는 그렇다면 위에서 언급한 연결리스트 저장소를 사용해야 하는데, 연결리스트에 저장되는 순서대로 아래쪽에 데이터를 저장하면 됩니다.

연결된 리스트의 길이가 8보다 크면 연결된 리스트에 대해 "트리" 작업을 수행하고 이를 레드-블랙 트리로 변환합니다.

하지만 연결 리스트의 길이가 6보다 작을 때만 레드-블랙 트리를 연결 리스트로 다시 변환한다는 점에 유의하세요. 이 프로세스를 "체이닝"이라고 합니다.

 

해시 테이블 관련 지식이 있나요?

해시 테이블(해시 테이블), 우리는 보통 해시 테이블이라고 부릅니다. 해시 테이블은 키 값을 기반으로 직접 접근할 수 있는 데이터 구조이다.

해시 함수는 본질적으로 함수이므로 hash(key)로 정의하며, key는 요소의 키 값, 해시 함수를 통해 얻은 값이 해시 값입니다.

해시 함수 요구 사항

1. 해시 함수는 너무 복잡해서는 안 되며, 너무 복잡하면 시간이 더 많이 소모되어 해시 테이블의 성능에 영향을 미칠 것입니다.

2. 해시 함수를 통해 얻은 해시 값은 가능한 한 무작위적이고 균등하게 분포되어 해시 충돌을 줄입니다. 충돌이 있더라도 각 위치에 해당하는 요소는 상대적으로 평균이므로 충돌이 발생하지 않습니다. 너무 많지만 일부는 특별할 것입니다. 상황은 거의 없습니다.

 

해시 테이블을 구성할 때 필연적으로 해시 충돌이 발생하는데, 이 문제를 해결하기 위한 두 가지 방법이 있습니다.

1. 개방형 주소 지정 방법

개발된 주소 지정 방법은 해시 충돌이 발생하면 사용 가능한 위치를 다시 탐색하여 삽입하는 것입니다. 일반적인 개방형 주소 지정 방법에는 선형 감지 및 2차 탐색이 포함됩니다.

2. 연결리스트 방식

연결된 목록 방법은 해시 충돌을 해결하는 데 더 일반적으로 사용되는 방법이며 개방형 주소 지정 방법보다 간단합니다. 해시 테이블의 각 첨자 위치는 연결 리스트에 해당하며, 해시 함수로 얻은 동일한 해시 값을 갖는 모든 요소는 첨자 위치에 해당하는 연결 목록에 배치됩니다.

 

두 객체가 동일한지 확인하는 방법

같음을 사용하세요. 기본적으로 같음은 ==와 동일한 기능을 가지고 있습니다. 두 개체의 주소가 같은지 비교합니다. 다시 작성하여 개체 비교를 수행할 수 있습니다.

 

디자인 패턴 중 팩토리 패턴과 옵저버 패턴에 대해 이야기해보겠습니다.

간단한 팩토리 패턴

팩토리 패턴의 설명은 매우 생생합니다. 객체 클래스를 만드는 것은 공장과 같으며, 생성해야 하는 객체는 제품입니다. 제품은 공장에서 가공되며, 제품을 사용하는 사람들은 제품이 어떻게 작동하는지 신경 쓸 필요가 없습니다. 제품이 생산됩니다. 소프트웨어 개발 관점에서 볼 때 이는 모듈 간의 결합을 효과적으로 줄입니다.

 

8a5d0deb0c449f53ac05f7c0caa65645.png

팩토리 패턴

객체 생성을 위한 인터페이스를 정의하고 해당 하위 클래스가 인스턴스화할 팩토리 클래스를 결정하도록 합니다. 팩토리 패턴은 하위 클래스가 생성될 때까지 생성 프로세스를 지연합니다.

특정 프로세스: 인터페이스 생성, 인터페이스를 구현하는 엔터티 클래스 생성, 주어진 정보를 기반으로 엔터티 클래스의 객체를 생성하는 팩토리 생성, 팩토리를 사용하여 유형 정보를 전달하여 해당 팩토리 및 엔터티 클래스의 객체 생성,

 

추상 공장 패턴

추상 팩토리 패턴은 슈퍼 팩토리 주변에 다른 팩토리를 만듭니다. 기가팩토리는 다른 공장 중의 공장이라고도 불립니다. 이러한 유형의 디자인 패턴은 창조적인 패턴입니다. 메르세데스-벤츠의 공장은 공장 방식과 달리 특정 제품만 생산하는 것이 아니라 제품군을 생산한다.

 

팩토리 모드의 차이점

  • 단순 팩토리: 팩토리 객체를 사용하여 동일한 계층 구조로 제품을 생산합니다. (확장 및 추가 제품은 지원되지 않습니다)
  • 팩토리 방법: 여러 팩토리 객체를 사용하여 동일한 계층 구조에서 해당 고정 제품을 생성합니다. (확장 및 추가 제품 지원)
  • 추상 팩토리: 여러 팩토리 객체를 사용하여 다양한 제품군의 모든 제품을 생산합니다. (제품 확장 및 추가는 지원되지 않습니다. 제품군 추가는 지원됩니다.)

 

관찰자 패턴

객체 간의 일대다 종속 관계를 정의합니다. 객체의 상태가 변경되면 해당 객체에 종속된 모든 객체에 알림이 전송되고 자동으로 업데이트됩니다.

예: 경매 중에 경매인은 최고 입찰가를 관찰한 다음 다른 입찰자에게 입찰하도록 알립니다. redis 감시 모드는 마스터 노드를 감독합니다.



저자: Xiao Yi
링크: Ali Yiyi Mian Jing_Niuke.com
출처: Niuke.com

 

 

 

추천

출처blog.csdn.net/qq_51118755/article/details/135307940