diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index 2b7d58519edd..f0fb551af42f 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -32,7 +32,7 @@ com.iluwatar 1.26.0-SNAPSHOT - abstract-document + korean org.junit.jupiter diff --git a/korean/src/Translator.java b/korean/src/Translator.java new file mode 100644 index 000000000000..ddee281ce6bd --- /dev/null +++ b/korean/src/Translator.java @@ -0,0 +1,41 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +public class Translator { + + private TranslationService translationService; + + public Translator(TranslationService translationService) { + this.translationService = translationService; + } + + public String translateToGerman(String englishText) { + return translationService.translate(englishText, "de"); + } +} + +// Hypothetical TranslationService interface +interface TranslationService { + String translate(String text, String targetLanguage); +} \ No newline at end of file diff --git a/singleton/README.md b/singleton/README.md index 626a2ed659bf..0a7b5905760e 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -50,8 +50,7 @@ Then in order to use: ```java var enumIvoryTower1 = EnumIvoryTower.INSTANCE; var enumIvoryTower2 = EnumIvoryTower.INSTANCE; - LOGGER.info("enumIvoryTower1={}", enumIvoryTower1); - LOGGER.info("enumIvoryTower2={}", enumIvoryTower2); + ``` The console output diff --git a/singleton/README_IN_KOREAN.md b/singleton/README_IN_KOREAN.md new file mode 100644 index 000000000000..4b2d2de7d947 --- /dev/null +++ b/singleton/README_IN_KOREAN.md @@ -0,0 +1,94 @@ +--- +title: "자바에서 싱글턴 패턴: 글로벌 액세스 포인트 구현" +shortTitle: 싱글턴 +description: "자바 애플리케이션에서 리소스의 효율적 사용과 쉬운 접근을 보장하는 싱글턴 패턴에 대해 알아보세요. 예제와 상세한 설명으로 싱글턴 패턴을 구현하는 방법을 제공합니다." +category: Creational +language: ko +tag: + - GoF (Gang of Four) + - 인스턴스화 + - 지연 초기화 + - 리소스 관리 +--- + +## 다른 이름 + +* 단일 인스턴스 + +## 싱글턴 디자인 패턴의 의도 + +자바 클래스가 단일 인스턴스만 가지도록 보장하고, 이 싱글턴 인스턴스에 대한 글로벌 액세스 지점을 제공합니다. + +## 싱글턴 패턴과 현실 세계의 예시 + +### 현실 세계의 예 + +> 현실 세계에서 싱글턴 패턴을 비유할 수 있는 사례는 정부가 발급하는 여권입니다. 한 나라에서 각 시민은 단 한 개의 유효한 여권만 발급받을 수 있습니다. 여권 발급 기관은 동일한 사람이 중복된 여권을 발급받지 못하도록 보장합니다. 시민이 여행할 때마다 이 단일 여권을 사용해야 하며, 이는 여행 자격을 나타내는 고유하고 글로벌하게 인정받는 식별자로 작동합니다. 이와 마찬가지로, 싱글턴 패턴은 자바 애플리케이션에서 효율적인 객체 관리를 보장합니다. + +### 쉽게 설명하자면 + +> 특정 클래스의 객체가 하나만 생성되도록 보장합니다. + +### 위키백과 설명 + +> 소프트웨어 엔지니어링에서 싱글턴 패턴은 클래스의 인스턴스화를 하나의 객체로 제한하는 소프트웨어 디자인 패턴입니다. 시스템 전체에서 동작을 조율해야 하는 경우 유용합니다. + +## 자바에서 싱글턴 패턴의 프로그래밍 예제 + +**Joshua Bloch, Effective Java 2nd Edition, p.18** + +> 단일 요소 열거형(enum) 타입이 싱글턴을 구현하는 가장 좋은 방법입니다. + +```java +public enum EnumIvoryTower { + INSTANCE +} +사용하려면 다음과 같이 작성합니다: +var enumIvoryTower1 = EnumIvoryTower.INSTANCE; +var enumIvoryTower2 = EnumIvoryTower.INSTANCE; +LOGGER.info("enumIvoryTower1={}", enumIvoryTower1); +LOGGER.info("enumIvoryTower2={}", enumIvoryTower2); + +콘솔 출력: +enumIvoryTower1=com.iluwatar.singleton.EnumIvoryTower@1221555852 +enumIvoryTower2=com.iluwatar.singleton.EnumIvoryTower@1221555852 + +자바에서 싱글턴 패턴을 사용할 때 +다음과 같은 경우 싱글턴 패턴을 사용하세요: + +클래스의 인스턴스가 정확히 하나만 있어야 하며, 잘 알려진 액세스 포인트에서 클라이언트가 이 인스턴스에 접근해야 할 때 +단일 인스턴스가 서브클래싱으로 확장 가능해야 하며, 클라이언트가 코드를 수정하지 않고 확장된 인스턴스를 사용할 수 있어야 할 때 + +자바에서 싱글턴 패턴의 현실 세계 응용 사례 +로깅 클래스 +애플리케이션 구성 클래스 +연결 풀(Connection Pool) +파일 관리자 +java.lang.Runtime#getRuntime() +java.awt.Desktop#getDesktop() +java.lang.System#getSecurityManager() +싱글턴 패턴의 장점과 단점 +장점: +단일 인스턴스에 대한 제어된 접근 제공 +네임스페이스 오염 감소 +작업과 표현의 세분화 가능 +필요한 경우 여러 인스턴스를 허용 +클래스 작업보다 유연함 +단점: +글로벌 상태로 인해 테스트가 어려움 +복잡한 수명 관리가 필요할 수 있음 +동기화 없이 사용할 경우 병렬 처리 환경에서 병목 현상을 초래할 수 있음 +관련 자바 디자인 패턴 +추상 팩토리(Abstract Factory): 클래스가 단일 인스턴스를 가지도록 보장할 때 사용 +팩토리 메서드(Factory Method): 싱글턴 패턴은 생성 로직을 캡슐화하는 팩토리 메서드를 사용하여 구현 가능 +프로토타입(Prototype): 인스턴스 생성을 피하고 고유 인스턴스를 관리하며 싱글턴과 함께 작동 가능 + 참고 문헌 및 크레딧 +Design Patterns: Elements of Reusable Object-Oriented Software +Effective Java +Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software +Java Design Patterns: A Hands-On Experience with Real-World Examples +Refactoring to Patterns + + + + diff --git a/singleton/pom.xml b/singleton/pom.xml index c6e7a65ca126..c11b08a17e19 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -26,37 +26,39 @@ --> - 4.0.0 - - com.iluwatar - java-design-patterns - 1.26.0-SNAPSHOT - - singleton - - - org.junit.jupiter - junit-jupiter-engine - test - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - - - - com.iluwatar.singleton.App - - - - - - - - + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + singleton + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.singleton.App + + + + + + + + diff --git a/singleton/src/main/java/com/iluwatar/singleton/Singleton.java b/singleton/src/main/java/com/iluwatar/singleton/Singleton.java new file mode 100644 index 000000000000..11ae09c24948 --- /dev/null +++ b/singleton/src/main/java/com/iluwatar/singleton/Singleton.java @@ -0,0 +1,43 @@ +package com.iluwatar.singleton; + +/** + * 싱글턴 패턴. + * 싱글턴 패턴은 클래스의 인스턴스가 하나만 존재하도록 보장하며, + * 그 인스턴스에 대한 전역 접근 지점을 제공합니다. + */ +public class Singleton { + + // Singleton 클래스의 유일한 인스턴스 + private static Singleton 인스턴스; + + // 새로운 인스턴스 생성을 방지하기 위한 private 생성자 + private Singleton() { + System.out.println("Singleton 인스턴스가 생성되었습니다."); + } + + /** + * Singleton 클래스의 유일한 인스턴스를 가져오는 메서드. + * 인스턴스가 아직 생성되지 않은 경우, 이 시점에서 생성됩니다. + * + * @return Singleton의 유일한 인스턴스. + */ + public static Singleton 가져오기() { + if (인스턴스 == null) { + 인스턴스 = new Singleton(); + } + return 인스턴스; + } + + /** + * Singleton 인스턴스에서 호출 가능한 메서드 예제. + */ + public void 작업하기() { + System.out.println("Singleton 인스턴스가 작동 중입니다."); + } + + // Singleton 패턴을 테스트하기 위한 메인 메서드 + public static void main(String[] args) { + Singleton singleton = Singleton.가져오기(); + singleton.작업하기(); + } +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/BillPughImplementationTest.java b/singleton/src/test/java/com/iluwatar/singleton/BillPughImplementationTest.java index 0b00cfe223e9..911217fabc77 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/BillPughImplementationTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/BillPughImplementationTest.java @@ -1,39 +1,14 @@ -/* - * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). - * - * The MIT License - * Copyright © 2014-2022 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package com.iluwatar.singleton; /** - * BillPughImplementationTest - * + * Bill Pugh 싱글턴 구현 테스트 클래스. */ -public class BillPughImplementationTest - extends SingletonTest{ - /** - * Create a new singleton test instance using the given 'getInstance' method. - */ - public BillPughImplementationTest() { - super(BillPughImplementation::getInstance); - } +public class BillPughImplementationTest extends SingletonTest { + + /** + * 주어진 'getInstance' 메서드를 사용하여 싱글턴 테스트 인스턴스를 생성합니다. + */ + public BillPughImplementationTest() { + super(BillPughImplementation::getInstance); + } } diff --git a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java index 7093ffc7307f..cc68868d07c9 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java @@ -1,27 +1,3 @@ -/* - * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). - * - * The MIT License - * Copyright © 2014-2022 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ package com.iluwatar.singleton; import static java.time.Duration.ofMillis; @@ -38,72 +14,70 @@ import org.junit.jupiter.api.Test; /** - *

This class provides several test case that test singleton construction.

+ *

싱글턴 패턴 테스트 클래스.

* - *

The first proves that multiple calls to the singleton getInstance object are the same when - * called in the SAME thread. The second proves that multiple calls to the singleton getInstance - * object are the same when called in the DIFFERENT thread.

+ *

첫 번째 테스트는 동일한 스레드 내에서 getInstance 메서드가 항상 동일한 객체를 반환하는지 확인합니다.

+ *

두 번째 테스트는 서로 다른 스레드에서 getInstance 메서드가 동일한 객체를 반환하는지 확인합니다.

* - * @param Supplier method generating singletons + * @param 싱글턴을 생성하는 Supplier 메서드 */ abstract class SingletonTest { /** - * The singleton's getInstance method. + * 싱글턴 클래스의 getInstance 메서드를 제공하는 Supplier. */ - private final Supplier singletonInstanceMethod; + private final Supplier 싱글턴인스턴스메서드; /** - * Create a new singleton test instance using the given 'getInstance' method. + * 주어진 'getInstance' 메서드를 사용하여 싱글턴 테스트 인스턴스를 생성합니다. * - * @param singletonInstanceMethod The singleton's getInstance method + * @param 싱글턴인스턴스메서드 싱글턴의 getInstance 메서드 */ - public SingletonTest(final Supplier singletonInstanceMethod) { - this.singletonInstanceMethod = singletonInstanceMethod; + public SingletonTest(final Supplier 싱글턴인스턴스메서드) { + this.싱글턴인스턴스메서드 = 싱글턴인스턴스메서드; } /** - * Test the singleton in a non-concurrent setting. + * 동일한 스레드에서 여러 번 호출 시 동일한 객체가 반환되는지 테스트합니다. */ @Test - void testMultipleCallsReturnTheSameObjectInSameThread() { - // Create several instances in the same calling thread - var instance1 = this.singletonInstanceMethod.get(); - var instance2 = this.singletonInstanceMethod.get(); - var instance3 = this.singletonInstanceMethod.get(); - // now check they are equal - assertSame(instance1, instance2); - assertSame(instance1, instance3); - assertSame(instance2, instance3); + void 동일_스레드_테스트() { + // 동일한 스레드에서 여러 인스턴스를 생성 + var instance1 = this.싱글턴인스턴스메서드.get(); + var instance2 = this.싱글턴인스턴스메서드.get(); + var instance3 = this.싱글턴인스턴스메서드.get(); + + // 동일한 인스턴스인지 확인 + assertSame(instance1, instance2, "첫 번째와 두 번째 인스턴스는 동일해야 합니다."); + assertSame(instance1, instance3, "첫 번째와 세 번째 인스턴스는 동일해야 합니다."); + assertSame(instance2, instance3, "두 번째와 세 번째 인스턴스는 동일해야 합니다."); } /** - * Test singleton instance in a concurrent setting. + * 서로 다른 스레드에서 여러 번 호출 시 동일한 객체가 반환되는지 테스트합니다. */ @Test - void testMultipleCallsReturnTheSameObjectInDifferentThreads() { + void 다중_스레드_테스트() { assertTimeout(ofMillis(10000), () -> { - // Create 10000 tasks and inside each callable instantiate the singleton class - final var tasks = IntStream.range(0, 10000) - .>mapToObj(i -> this.singletonInstanceMethod::get) + // 10000개의 작업 생성, 각 작업에서 싱글턴 인스턴스를 생성 + final var 작업들 = IntStream.range(0, 10000) + .>mapToObj(i -> this.싱글턴인스턴스메서드::get) .collect(Collectors.toCollection(ArrayList::new)); - // Use up to 8 concurrent threads to handle the tasks - final var executorService = Executors.newFixedThreadPool(8); - final var results = executorService.invokeAll(tasks); + // 최대 8개의 동시 실행 스레드를 사용하여 작업 처리 + final var 실행서비스 = Executors.newFixedThreadPool(8); + final var 결과들 = 실행서비스.invokeAll(작업들); - // wait for all the threads to complete - final var expectedInstance = this.singletonInstanceMethod.get(); - for (var res : results) { - final var instance = res.get(); - assertNotNull(instance); - assertSame(expectedInstance, instance); + // 모든 스레드가 완료될 때까지 대기 + final var 예상_인스턴스 = this.싱글턴인스턴스메서드.get(); + for (var 결과 : 결과들) { + final var 인스턴스 = 결과.get(); + assertNotNull(인스턴스, "각 스레드에서 반환된 인스턴스는 null이 아니어야 합니다."); + assertSame(예상_인스턴스, 인스턴스, "모든 스레드에서 동일한 인스턴스가 반환되어야 합니다."); } - // tidy up the executor - executorService.shutdown(); + // Executor 종료 + 실행서비스.shutdown(); }); - } - } diff --git a/update-header.sh b/update-header.sh index 48da4dcd6125..568d00d52a03 100755 --- a/update-header.sh +++ b/update-header.sh @@ -1,4 +1,29 @@ #!/bin/bash +# +# This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). +# +# The MIT License +# Copyright © 2014-2022 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + # Find all README.md files in subdirectories one level deep # and replace "### " with "## " at the beginning of lines diff --git a/visitor/src/main/java/com/iluwatar/visitor/example/translate/Singleton.java b/visitor/src/main/java/com/iluwatar/visitor/example/translate/Singleton.java new file mode 100644 index 000000000000..50200268b833 --- /dev/null +++ b/visitor/src/main/java/com/iluwatar/visitor/example/translate/Singleton.java @@ -0,0 +1,43 @@ +package com.iluwatar.visitor.example.translate; + +/** + * 싱글턴 패턴. + * 싱글턴 패턴은 클래스의 인스턴스가 하나만 존재하도록 보장하며, + * 그 인스턴스에 대한 전역 접근 지점을 제공합니다. + */ +public class Singleton { + + // Singleton 클래스의 유일한 인스턴스 + private static Singleton 인스턴스; + + // 새로운 인스턴스 생성을 방지하기 위한 private 생성자 + private Singleton() { + System.out.println("Singleton 인스턴스가 생성되었습니다."); + } + + /** + * Singleton 클래스의 유일한 인스턴스를 가져오는 메서드. + * 인스턴스가 아직 생성되지 않은 경우, 이 시점에서 생성됩니다. + * + * @return Singleton의 유일한 인스턴스. + */ + public static Singleton 가져오기() { + if (인스턴스 == null) { + 인스턴스 = new Singleton(); + } + return 인스턴스; + } + + /** + * Singleton 인스턴스에서 호출 가능한 메서드 예제. + */ + public void 작업하기() { + System.out.println("Singleton 인스턴스가 작동 중입니다."); + } + + // Singleton 패턴을 테스트하기 위한 메인 메서드 + public static void main(String[] args) { + Singleton singleton = Singleton.가져오기(); + singleton.작업하기(); + } +} diff --git a/visitor/src/main/java/translateExample/koreantrans.java b/visitor/src/main/java/translateExample/koreantrans.java new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/visitor/src/test/java/com/iluwatar/visitor/example/SingletonTese.java b/visitor/src/test/java/com/iluwatar/visitor/example/SingletonTese.java new file mode 100644 index 000000000000..b8249ee70ba0 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/example/SingletonTese.java @@ -0,0 +1,84 @@ +package com.iluwatar.visitor.example; + + +import static java.time.Duration.ofMillis; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTimeout; + +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; + +/** + *

싱글턴 패턴 테스트 클래스.

+ * + *

첫 번째 테스트는 동일한 스레드 내에서 getInstance 메서드가 항상 동일한 객체를 반환하는지 확인합니다.

+ *

두 번째 테스트는 서로 다른 스레드에서 getInstance 메서드가 동일한 객체를 반환하는지 확인합니다.

+ * + * @param 싱글턴을 생성하는 Supplier 메서드 + */ +abstract class SingletonTest { + + /** + * 싱글턴 클래스의 getInstance 메서드를 제공하는 Supplier. + */ + private final Supplier 싱글턴인스턴스메서드; + + /** + * 주어진 'getInstance' 메서드를 사용하여 싱글턴 테스트 인스턴스를 생성합니다. + * + * @param 싱글턴인스턴스메서드 싱글턴의 getInstance 메서드 + */ + public SingletonTest(final Supplier 싱글턴인스턴스메서드) { + this.싱글턴인스턴스메서드 = 싱글턴인스턴스메서드; + } + + /** + * 동일한 스레드에서 여러 번 호출 시 동일한 객체가 반환되는지 테스트합니다. + */ + @Test + void 동일_스레드_테스트() { + // 동일한 스레드에서 여러 인스턴스를 생성 + var instance1 = this.싱글턴인스턴스메서드.get(); + var instance2 = this.싱글턴인스턴스메서드.get(); + var instance3 = this.싱글턴인스턴스메서드.get(); + + // 동일한 인스턴스인지 확인 + assertSame(instance1, instance2, "첫 번째와 두 번째 인스턴스는 동일해야 합니다."); + assertSame(instance1, instance3, "첫 번째와 세 번째 인스턴스는 동일해야 합니다."); + assertSame(instance2, instance3, "두 번째와 세 번째 인스턴스는 동일해야 합니다."); + } + + /** + * 서로 다른 스레드에서 여러 번 호출 시 동일한 객체가 반환되는지 테스트합니다. + */ + @Test + void 다중_스레드_테스트() { + assertTimeout(ofMillis(10000), () -> { + // 10000개의 작업 생성, 각 작업에서 싱글턴 인스턴스를 생성 + final var 작업들 = IntStream.range(0, 10000) + .>mapToObj(i -> this.싱글턴인스턴스메서드::get) + .collect(Collectors.toCollection(ArrayList::new)); + + // 최대 8개의 동시 실행 스레드를 사용하여 작업 처리 + final var 실행서비스 = Executors.newFixedThreadPool(8); + final var 결과들 = 실행서비스.invokeAll(작업들); + + // 모든 스레드가 완료될 때까지 대기 + final var 예상_인스턴스 = this.싱글턴인스턴스메서드.get(); + for (var 결과 : 결과들) { + final var 인스턴스 = 결과.get(); + assertNotNull(인스턴스, "각 스레드에서 반환된 인스턴스는 null이 아니어야 합니다."); + assertSame(예상_인스턴스, 인스턴스, "모든 스레드에서 동일한 인스턴스가 반환되어야 합니다."); + } + + // Executor 종료 + 실행서비스.shutdown(); + }); + } +}