//
Search
Duplicate
📖

2장 객체 생성과 파괴

상태
진행 중
담당자
시작일
2023/11/04 07:17
최종편집일
2023/11/17 14:19
객체를 만들어야 할 때와 만들지 말아야 할때를 구분 하는 법, 올바른 객체 생성 방법과 불필요한 생성을 피하는 방법에 대해 정리한다.

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

장점
1.
이름을 가질수 있다.
BigInteger bigInteger = new BigInteger(int ,int, Random); BigInteger bigInteger = BigInteger.probablePrime();
JavaScript
복사
2.
호출할 때마다 인스턴스를 새로 생성하지는 않아도 된다.
Boolean isTrue = new Boolean(true); Boolean isFalse = new Boolean(false);
JavaScript
복사
Boolean isTrue = Boolean.valueOf(true); Boolean isfalse = Boolean.valueOf(false); 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); } }
JavaScript
복사
3.
반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.
public Member { public Member() {} } public RegularMember extends Member { public RegularMember() {} } public GeneralMember extends Member { public GeneralMember() {} } public MemberFactory { private static final String MEMBER_ERROR = "일치하는 회원이 없습니다."; public static Member of(Type type) { if (type.isRegularMember) { return new RegularMember(); } if (type.isGeneralMember) { return new GeneralMember(); } new throw MemberException(MEMBER_ERROR); } }
JavaScript
복사
4.
입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
// Shape 인터페이스 interface Shape { void draw(); } // Circle 클래스 class Circle implements Shape { @Override public void draw() { System.out.println("원 그리기"); } } // Rectangle 클래스 class Rectangle implements Shape { @Override public void draw() { System.out.println("사각형 그리기"); } } class ShapeFactory { public static Shape createShape(String shapeType) { if (shapeType.equalsIgnoreCase("원")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("사각형")) { return new Rectangle(); } else { return null; } } }
JavaScript
복사
5.
정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
단점
1.
상속을 하려면 public 이나 protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
2.
정적 팩터리 메서드는 프로그래머가 찾기 어렵다.
정적 팩터리 메서드를 사용하는 방법
from : 하나의 매개 변수를 받아서 객체를 생성
of : 여러개의 매개 변수를 받아서 객체를 생성
getInstance | instance : 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
newInstance | create : 새로운 인스턴스를 생성
get[OtherType] : 다른 타입의 인스턴스를 생성. 이전에 반환했던 것과 같을 수 있음.
new[OtherType] : 다른 타입의 새로운 인스턴스를 생성.
핵심정리 정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다. 그렇다고 하더라도 정적 팩터리를 사용하는게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자!.

생성자에 매개변수가 만다면 빌더를 고려하라

정적 팩터리와 생성자에는 제약이 하나 있다. 선택적 매개변수가 많을 때 적절히 대응하기 어렵다는 점이다.
public class Product { private String name; private String description; private double price; private int quantity; public Product(String name) { this.name = name; } public Product(String name, String description) { this(name); this.description = description; } public Product(String name, String description, double price) { this(name, description); this.price = price; } public Product(String name, String description, double price, int quantity) { this(name, description, price); this.quantity = quantity; } }
JavaScript
복사
보통 이런 생성자는 사용자가 설정하길 원치 않는 매개변수까지 포함하기 쉬운데, 어쩔 수 없이 그런 매개변수에도 값을 지정해줘야 한다. 점층적 생성자 패턴도 쓸 수는 있지만, 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. 다행히 우리에겐 점층적 생성자 패턴의 안정성과 자바빈즈 패턴의 가독성을 겸비한 빌더 패턴이 있다.
public class Product { private String name; private String description; private double price; private int quantity; private Product(Builder builder) { this.name = builder.name; this.description = builder.description; this.price = builder.price; this.quantity = builder.quantity; } public static class Builder { private String name; private String description; private double price; private int quantity; public Builder(String name) { this.name = name; } public Builder description(String description) { this.description = description; return this; } public Builder price(double price) { this.price = price; return this; } public Builder quantity(int quantity) { this.quantity = quantity; return this; } public Product build() { return new Product(this); } } }
JavaScript
복사
Product product = new Product.Builder("Sample Product") .description("This is a sample product.") .price(19.99) .quantity(100) .build();
JavaScript
복사
빌더 패턴은 계층적으로 설계된 클래스와 함께 쓰기에 좋다.
public abstract class Pizza{ public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE } final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> { EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class); public T addTopping(Topping topping) { toppings.add(Objects.requireNonNull(topping)); return self(); } abstract Pizza build(); protected abstract T self(); } Pizza(Builder<?> builder) { toppings = builder.toppings.clone(); } }
Java
복사
Pizza.Builder 클래스는 재귀적 타입 한정을 이용하는 제너릭 타이비다. 여기에 추상 메서드인 self
public class NyPizza extends Pizza { public enum Size { SMALL, MEDIUM, LARGE } private final Size size; public static class Builder extends Pizza.Builder<Builder> { private final Size size; public Builder(Size size) { this.size = Objects.requireNonNull(size); } @Override public NyPizza build() { return new NyPizza(this); } @Override protected Builder self() { return this; } } private NyPizza(Builder builder) { super(builder); size = builder.size; } }
Java
복사
public class Calzone extends Pizza { private final boolean sauceInside; public static class Builder extends Pizza.Builder<Builder> { private boolean sauceInside = false; public Builder sauceInside() { sauceInside = true; return this; } @Override public Calzone build() { return new Calzone(this); } @Override protected Builder self() { return this; } } private Calzone(Builder builder) { super(builder); sauceInside = builder.sauceInside; } }
Java
복사