translation

This is an AI translated post.

제이온

[Effective Java] Item 5. Use Dependency Injection instead of Hard-Coding Resources

Select Language

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

Summarized by durumis AI

  • If a class internally relies on one or more resources, it is not recommended to use static utility classes and singleton classes, and it is desirable to use dependency injection.
  • Using dependency injection can improve the class's flexibility, reusability, and testability, and resources can be injected from constructors, static factories, builders, etc.
  • Dependency injection can be used by passing the resource itself or the resource factory, and using a dependency injection framework is efficient for projects with many dependencies.

If a class internally relies on one or more resources, and those resources affect the class's behavior, it is best to avoid using singletons and static utility classes.


The class should not create these resources directly, instead, it is better to pass the necessary resources to the constructor. Dependency injection can improve the flexibility, reusability, and testability of the class.


Example

Example of using a static utility class

public class SpellChecker {

    private static final Lexicon dictionary = new Lexicon();

    private SpellChecker() {
    }

    public static boolean isValid(String word) {
        // Logic using dictionary
    }

    public static List suggestions(String typo) {
        // Logic using dictionary
    }

This utility class assumes that only one dictionary is used. However, in reality, dictionaries are often separated by language, and even specialized dictionaries are used separately for special vocabulary.


Example of using a singleton class

public class SpellChecker {

    private final Lexicon dictionary = new Lexicon();

    public static SpellChecker INSTANCE = new SpellChecker();

    private SpellChecker() {
    }

    public static boolean isValid(String word) {
        // Logic using dictionary
    }

    public static List suggestions(String typo) {
        // Logic using dictionary
    }


Similarly, singleton classes assume that only one dictionary is used, resulting in the same drawback as above.


Solution 1 - Remove the final keyword from the field.

public class SpellChecker {

    private Lexicon dictionary = new Lexicon();

    public static SpellChecker INSTANCE = new SpellChecker();

    private SpellChecker() {
    }

    public static void changeDictionary(Lexicon dictionary) {
        this.dictionary = dictionary;
    }

    public static boolean isValid(String word) {
        // Logic using dictionary
    }

    public static List suggestions(String typo) {
        // Logic using dictionary
    }


It is possible to remove the final keyword from the dictionary of a static utility class or singleton class and design it so that the dictionary can be replaced with a different dictionary from the outside. However, this method is awkward to use, and can cause concurrency issues in a multi-threaded environment.


Solution 2 - Use dependency injection.

public class SpellChecker {

    private final Lexicon dictionary;

    public SpellChecker(Lexicon dictionary) {
        this.dictionary = dictionary;
    }

    public static boolean isValid(String word) {
        // Logic using dictionary
    }

    public static List suggestions(String typo) {
        // Logic using dictionary
    }


Through the example above, we can see that static classes and singleton classes should not rely on internal resources. In other words, it is desirable to receive internal resources from the outside.


Classes that use dependency injection have the advantage of guaranteeing immutability thanks to the final keyword and supporting multiple resource instances.


Dependency injection can be applied not only to constructors but also to static factories and builders.


Dependency injection simply passes the resource itself, but it is also often used to pass a resource factory. A factory is an object that creates instances of a specific type repeatedly when called. This method is called the factory method pattern, and Supplier in Java 8 is a perfect example of a factory.


public static List create(Supplier generator) {
    ...


Usually, a bounded wildcard type is used to limit the type parameter of the factory. Using this method, the client can pass any factory that is a subtype of the type it specifies.


Dependency injection improves flexibility and testability, but it can be very expensive for projects with many dependencies. In such cases, the cost can be reduced by using dependency injection frameworks (Dagger, Guice, Spring, etc.).


Source

제이온
제이온
제이온
제이온
[Effective Java] Item 3. Ensure Singleton with Private Constructor or Enum Type This article introduces three ways to implement the Singleton pattern in Java (public static member, static factory method, enum type), explains the pros and cons of each method, and the precautions to be taken during serialization. It presents the enum t

April 27, 2024

[Effective Java] Item 1: Consider Static Factory Methods Instead of Constructors Static factory methods provide a flexible and efficient way to create instances instead of constructors. They can have names, return instances that meet specific conditions, and improve performance through caching. Unlike the singleton pattern, they can c

April 27, 2024

[Effective Java] Item 4. Use a private constructor if you want to prevent instantiation Utility classes that contain only static methods and fields should set the access modifier of the constructor to private to prevent instantiation. This prevents users from mistakenly assuming that the constructor is automatically generated and prevents in

April 28, 2024

Logical Data Modeling Logical data modeling is the process of transforming a conceptual data model into the relational database paradigm based on mapping rules, handling 1:1, 1:N, and N:M relationships, and ensuring data integrity through normalization. It refines tables throu
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

April 9, 2024

What kind of tests should you run for 1-person app development? Learn how to prioritize tests and develop an effective testing strategy when developing an app. The author prioritizes human testing, integration testing, unit testing, and acceptance/widget testing, emphasizing time efficiency. Check out practical testin
Alien Story
Alien Story
Alien Story
Alien Story
Alien Story

May 16, 2024

Relational Data Modeling Relational data modeling is the process of dividing real-world information into tables and data, going through the stages of requirement analysis, conceptual data modeling, logical data modeling, and physical data modeling. Conceptual modeling is visualiz
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

April 8, 2024

[Non-Major, Surviving as a Developer] 12. English for New Developers New developers are advised to prioritize learning IT-related terminology and syntax for improving their English skills. Understanding terms commonly used in the field, such as back-end, front-end, API, SDK, etc., can help them adapt quickly to development
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 1, 2024

[python] Python Basics 1: Understanding Python Modules Python modules are files that contain variables, functions, and classes, and are useful when using modules created by others or when grouping commonly used variables, functions, etc. You can use the `import` keyword to import and use modules, and you can
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

March 27, 2024

Mocking Prisma Client for Unit Testing in NestJS Removing external dependencies is essential for application unit testing. You can easily perform unit testing using Prisma ORM's Jest mocking method. By installing the jest-mock-extended package and mocking Prisma Client, you can build a convenient testin
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

April 2, 2024