이펙티브 자바 아이템1

한주영·2023년 9월 5일
0

이펙티브자바

목록 보기
1/33

생성자 대신 정적 패터리 메서드를 고려하라

클래스의 인스턴스를 얻는 전통적인 수단은 public생서자다
클래스는 생성자와 별도로 정적 팩터리 메서드를 제공할수 있다

정적 팩터리 메서드가 생성자 보다 좋은점

1. 이름을 가질수있다

public class Student {

    private String name;
    private int age;
    private char gender;

    // 디폴트 생성자
    public Student(){

    }

    //매개변수 생성자
    public Student(String name) {
        this.name = name;
    }

    //팩토리메서드를 이용한 초기화
    public static Student createStudentName(String name){
        return new Student(name);
    }
}

2. 호출될때마다 인스턴스를 새로 생성하지 않아도 된다

public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
    ...

객체 안에 미리 정의된 static final 상수 객체를 반환, 매번 새로운 객체를 만들지 않는다.

3. 반환 타입의 하위 타입의 객체를 반환할 수 있는 능력이 있다.

class Animal {
    public void speak() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Dog barks");
    }

    public void fetch() {
        System.out.println("Dog fetches a ball");
    }
}

class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Cat meows");
    }

    public void scratch() {
        System.out.println("Cat scratches");
    }
}

public class AnimalTest {
    public static Animal getAnimal(String type) {
        if ("dog".equalsIgnoreCase(type)) {
            return new Dog();
        } else if ("cat".equalsIgnoreCase(type)) {
            return new Cat();
        } else {
            return new Animal(); 
        }
    }

    public static void main(String[] args) {
        Animal animal1 = getAnimal("dog");
        Animal animal2 = getAnimal("cat");

        animal1.speak(); // 출력값: Dog barks
        animal2.speak(); // 출력값: Cat meows

        
        if (animal1 instanceof Dog) {
            ((Dog) animal1).fetch(); // 출력값: Dog fetches a ball
        } else if (animal1 instanceof Cat) {
            ((Cat) animal1).scratch();
        }

        if (animal2 instanceof Dog) {
            ((Dog) animal2).fetch();
        } else if (animal2 instanceof Cat) {
            ((Cat) animal2).scratch(); // 출력값: Cat scratches
        }
    }
}

이 예시는 Animal 클래스와 Dog 및 Cat이라는 두 개의 하위 클래스가 있다.
getAnimal 메소드는 Animal 클래스의 객체를 반환하지만 , Animal의 하위 유형이기 때문에 실제로 Dog 또는 Cat의 인스턴스도 반환할 수 있다.
각각 'Dog'와 'Cat'의 인스턴스인 'animal1'과 'animal2' 모두에서 'speak' 메서드를 호출하여 이를 보여준다.

  1. 입력 매개변수에따라 매번 다른 클래스의 객체를 반환할 수 있다.

반환타이의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하던 상관없다

class Shape {

    public void draw(){
        System.out.println("모양을 그리다");
    }


}

class Circle extends Shape{

    @Override
    public void draw() {

        System.out.println("원을 그리다");
    }
}

class Retangle extends Shape
{
    @Override
    public void draw() {
        System.out.println("직사각형을 그리다");
    }
}
public class ShapeFactory{

    public Shape createShape(String shapeType){

        if("circle".equalsIgnoreCase(shapeType)){
            return new Circle();
        } else if ("retangle".equalsIgnoreCase(shapeType)) {
           return new Retangle();
        }else {
            return new Shape();
        }
    }

    public static void main(String[] args) {

        ShapeFactory shapeFactory= new ShapeFactory();

        Shape shape1= shapeFactory.createShape("circle");
        Shape shape2= shapeFactory.createShape("retangle");
        Shape shape3= shapeFactory.createShape("triangle");

        shape1.draw();
        shape2.draw();
        shape3.draw();

    }
}

위 예시는 Shape 기본 클래스와 Circle 및 Rectangle이라는 두 개의 하위 클래스가 있고, ShapeFactory 클래스에는 shapeType 매개변수를 사용하는 createShape 메서드가 있다. shapeType 값에 따라 적절한 클래스(Circle, Rectangle 또는 기본 Shape)의 인스턴스를 반환한다.

다양한 입력 매개변수로 createShape를 호출하면 다양한 클래스의 객체를 반환할 수 있다.
이는 Java에서 메소드 오버로딩을 사용하여 입력 매개변수를 기반으로 다양한 클래스의 객체를 반환하는 방법을 보여주고있다.

5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재 하지 않아도 된다

Java에서는 리플렉션 및 동적 클래스 로딩을 사용하여 컴파일 타임에 존재할 필요가 없는 클래스의 인스턴스를 생성할 수 있다

Shape라는 인터페이스가 있다고 가정할때

public interface Shape {
    void draw();
}

이제 컴파일 타임에 해당 클래스가 알려지지 않은 경우에도 이 'Shape' 인터페이스를 구현하는 클래스의 인스턴스를 반환할 수 있는 정적 팩토리 메서드를 생성.
리플렉션 및 동적 클래스 로딩을 사용하여 구현할수있다.

import java.lang.reflect.InvocationTargetException;

public class ShapeFactory {
    public static Shape createShape(String className) {
        try {
            //리플렉션을 사용하여 클래스를 동적으로 로드
            Class<?> shapeClass = Class.forName(className);

            //클래스의 인스턴스 만들기
            Object instance = shapeClass.getDeclaredConstructor().newInstance();

            // 인스턴스가 Shape 객체인지 체크
            if (instance instanceof Shape) {
                return (Shape) instance;
            } else {
                throw new IllegalArgumentException("\n" + "클래스가 Shape 인터페이스를 구현하지 않습니다");
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                 | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        // 팩토리를 이용해 동적으로 생성
        Shape circle = ShapeFactory.createShape("Circle");
        Shape triangle = ShapeFactory.createShape("Triangle");

        if (circle != null) {
            circle.draw();
        } else {
            System.out.println("원을 만들수없다");
        }

        if (triangle != null) {
            triangle.draw();
        } else {
            System.out.println("삼각형을 만들수 없다");
        }
    }
}
profile
백엔드개발자가 되고싶은 코린이:)

0개의 댓글