ProgramingTip

Java에서 여러 생성 손상을 처리하는 가장 좋은 방법

bestdevel 2020. 10. 25. 12:42
반응형

Java에서 여러 생성 손상을 처리하는 가장 좋은 방법


Java에서 여러 생성 손상을 처리하는 가장 좋은 (즉, 가장 깨끗하고 / 안전하고 / 가장 효율적인 방법) 무엇인지 궁금합니다. 특히 하나 이상의 생성자에서 모든 필드가 지정되지 않은 경우 :

public class Book
{

    private String title;
    private String isbn;

    public Book()
    {
      //nothing specified!
    }

    public Book(String title)
    {
      //only title!
    }

    ...     

}

필드가 지정되지 않은 경우 어떻게해야합니까? 지금까지 클래스에서 언급을 사용하여 필드가 null이되지 않습니다. 그러나 이것이 "좋은"작업 방법입니까?


약간의 단순화 된 답변 :

public class Book
{
    private final String title;

    public Book(String title)
    {
      this.title = title;
    }

    public Book()
    {
      this("Default Title");
    }

    ...
}

빌더 패턴 사용을 고려하십시오. 매개 변수에 초기화를 설정하고 명확하고 간결한 방식으로 초기화 할 수 있습니다. 예를 들면 :


    Book b = new Book.Builder("Catcher in the Rye").Isbn("12345")
       .Weight("5 pounds").build();

편집 : 또한 여러 개의자가 생성자가 필요하지 않습니다.


클래스 불변이 무엇인지 지정해야합니다. 즉, 클래스의 인스턴스에 대해 항상 참이되는 속성 (예 : 책 제목이 null이 아니거나 개 크기가 항상> 0이됩니다).

반드시 불변성은 구성되어야하며, 수명 동안 보존되어야합니다. 즉, 메서드가 불변성을 갖습니다. 설치자는 필수 인수를 설치합니다.

class Book {
    private String title; // not nullable
    private String isbn;  // nullable

    // Here we provide a default value, but we could also skip the 
    // parameterless constructor entirely, to force users of the class to
    // provide a title
    public Book()
    {
        this("Untitled"); 
    }

    public Book(String title) throws IllegalArgumentException
    {
        if (title == null) 
            throw new IllegalArgumentException("Book title can't be null");
        this.title = title;
        // leave isbn without value
    }
    // Constructor with title and isbn
}

그러나 그러나 성사 불변의 선택은 작성하는 클래스, 사용의 선택 따라 크게 달라 지 질문에 대한 확실한 답은 없습니다.


항상 유효하고 합법적 인 개체를 구성해야합니다. 생성자 매개 변수를 사용할 수있는 경우 빌더 개체를 사용하여 하나를 추가하며 개체가 완료 될 때만 빌더에서 개체를 해제해야합니다.

생성자 사용에 대한 질문 : 저는 항상 다른 모든 생성자가 연기하는 하나의 기본 생성 작업을 수행합니다. "생략 된"매개 변수를 다음 논리 생성자에 연결하고 기본 생성자에서 끝납니다. 그래서 :

class SomeClass
{
SomeClass() {
    this("DefaultA");
    }

SomeClass(String a) {
    this(a,"DefaultB");
    }

SomeClass(String a, String b) {
    myA=a;
    myB=b;
    }
...
}

이것은 모든 생성자가 연기하는 private init () 메소드를 사용합니다.

그리고 생성자와 매개 변수의 수를 작게 유지하십시오 (가이드 라인으로 최대 5 개).


일반적인 생성자 팁 :

  • 모든 초기화를 단일 생성자에 집중하고 다른 생성자에서 호출하십시오.
    • 이것은 기본 매개 변수를 시뮬레이션하기 위해 작동합니다.
  • 생성자에서 최종이 아닌 메서드를 호출하지 않습니다.
    • 개인 메서드는 정의에 따라 최종입니다.
    • 다형성은 여기서 당신을 죽일 수 있습니다. 하위 클래스가 초기화되기 전에 하위 클래스 구현을 호출 할 수 있습니다.
    • "도우미"방법이 필요한 경우 비공개 또는 최종 방법으로 설정합니다.
  • super () 호출시 명시 적이어야합니다.
    • 얼마나 많은 자바 프로그래머가 슈퍼 ()가 호출되는 것이 확실하지 않을까요 (이 (...)).
  • 생성자에 대한 초기화 규칙의 순서를 알아야합니다. 기본적으로 다음과 가변합니다.

    1. 이 (...) 현재는 (경우 단지 다른 생성자로 이동)
    2. super (...) 호출 [명시 적이 지 않은 경우, 암시 적으로 super () 호출]
    3. (이 규칙을 재귀 적으로 사용하여 슈퍼 클래스를 생성)
    4. 선언을 통해 필드 초기화
    5. 현재 생성자의 실행 본문
    6. 이전 생성자로 돌아 가기 (이 (...) 호출이 발생하는 경우)

전체 흐름은 다음과 가변됩니다.

  • 수퍼 클래스 계층에서 Object로 이동합니다.
  • 완료되지 않은 동안
    • 필드 초기화
    • 생성자 본문 실행
    • 하위 클래스로 드롭 다운

악의 좋은 예를 보려면 다음이 인쇄 할 내용을 제공 한 다음 실행하십시오.

package com.javadude.sample;

/** THIS IS REALLY EVIL CODE! BEWARE!!! */
class A {
    private int x = 10;
    public A() {
        init();
    }
    protected void init() {
        x = 20;
    }
    public int getX() {
        return x;
    }
}

class B extends A {
    private int y = 42;
    protected void init() {
        y = getX();
    }
    public int getY() {
        return y;
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        System.out.println("x=" + b.getX());
        System.out.println("y=" + b.getY());
    }
}

위의 내용이 작동하는 이유를 설명하는 주석을 추가하겠습니다 ... 일부는 분명 할 수 있습니다. 일부는 ...


범위가 필요한 경우 또 다른 고려 사항은 생성자에서 확인을 수행합니다.

public Book(String title)
{
    if (title==null)
        throw new IllegalArgumentException("title can't be null");
    this.title = title;
}

생성자 대신 정적 팩토리 메소드를 사용하는 것을 고려해 볼 가치가 있습니다.

나는 대신 말하고 있지만 분명히 생성자를 바꿀 수는 없습니다 . 하지만 할 수있는 정적 팩토리입니다. 이렇게하면 정적 팩토리 메서드를 클래스 API의 일부로 게시하지만 동시에 생성 될 비공개 또는 패키지 비공개로 만듭니다.

(Joshua Bloch의 Effective Java 2nd Edition 에서 볼 수 특히 Gang of Four의 디자인 패턴이 정의 된 약간의 혼동 될 수 있습니다). 중첩 된 클래스, 빌더 개체 등을 만드는 것을 의미합니다.

이 접근 방식은 사용자와 클라이언트 사이에 추가 추상화 계층을 추가하여 캡슐화를 강화하고 더 쉽게 만듭니다. 또한 인스턴스 제어 기능을 제공합니다. 개체가 클래스 내에서 인스턴스화되기 때문에 클라이언트가 아닌 사용자가 우선 개체를 만드는시기와 방법을 결정합니다.

마지막으로, 테스트를 더 쉽게 만듭니다. 수행이나 유효성 검사를 수행하지 않고 필드에 값을 할당하는 멍청한 상태를 생성하여 시스템이 작동하고 반응하는지 테스트 할 수 있습니다. 생성자에서 데이터의 유효성을 검사하는 경우 수행 할 수 없습니다.

이에 대해 (이미 언급 한) Joshua Bloch의 Effective Java 2nd Edition 에서 더 많이 읽을 수 있습니다. 이것은 모든 개발자의 도구 상자에서 중요한 도구이며 책의 1 장의 주제 인 것은 당연합니다. ;-)

귀하의 예를 따르면 :

public class Book {

    private static final String DEFAULT_TITLE = "The Importance of Being Ernest";

    private final String title;
    private final String isbn;

    private Book(String title, String isbn) {
        this.title = title;
        this.isbn = isbn;
    }

    public static Book createBook(String title, String isbn) {
        return new Book(title, isbn);
    }

    public static Book createBookWithDefaultTitle(String isbn) {
        return new Book(DEFAULT_TITLE, isbn);
    }

    ...

}

어떤 방법을 선택하든 , 다른 생성자에서 사용하더라도 모든 값을 맹목적으로 할당하는 하나의 기본 생성자 를 갖는 것이 좋습니다 .


다음을 수행합니다.

공개 수업 도서
{
    개인 최종 문자열 제목;
    개인 최종 문자열 isbn;

    공개 도서 (최종 문자열 t, 최종 문자열 i)
    {
        if (t == null)
        {
            throw new IllegalArgumentException ( "t는 null 일 수 없음");
        }

        if (i == null)
        {
            throw new IllegalArgumentException ( "i can be null");
        }

        제목 = t;
        isbn = i;
    }
}

나는 여기서 다음과 같은 가정을하고있다.

1) 제목은 절대 변경되지 않습니다 (따라서 제목이 최종 임) 2) isbn은 변경되지 않습니다 (따라서 isbn이 최종적 임). 3) 제목과 isbn이 모두없는 책을 소유하는 것은 유효하지 않습니다.

학생 수업을 고려하십시오.

공개 수업 학생
{
    개인 최종 StudentID ID;
    private String firstName;
    개인 문자열 lastName;

    public Student (최종 StudentID i,
                   마지막 문자열 먼저,
                   마지막 문자열 마지막)
    {
        if (i == null)
        {
            throw new IllegalArgumentException ( "i can be null"); 
        }

        if (첫 번째 == null)
        {
            throw new IllegalArgumentException ( "first cannot be null"); 
        }

        if (마지막 == null)
        {
            throw new IllegalArgumentException ( "last cannot be null"); 
        }

        id = 나;
        firstName = 첫 번째;
        lastName = 마지막;
    }
}

학생은 ID, 이름 및 성으로 생성되어야합니다. 학생 ID는 절대 변경할 수 없지만 사람의 성과 이름은 변경 될 수 있습니다 (결혼, 내기 손실로 인한 이름 변경 등).

어떤 해석자를 가질 것인지 결정할 때 무엇이 ​​합리적인지에 대해 생각할 필요가 있습니다. 종종 사람들은 그들이 배웠기 때문에 set / get 메소드를 추가합니다. 그러나 그것은 종종 나쁜 생각입니다.

변경 불가능한 클래스는 변경 가능한 클래스보다 (최종 변수가있는 클래스) 갖는 것이 훨씬 낫습니다. 이 책 : http://books.google.com/books?id=ZZOiqZQIbRMC&pg=PA97&sig=JgnunNhNb8MYDcx60Kq4IyHUC58#PPP1,M1(Effective Java)에는 불변성에 대한 좋은 토론이 있습니다. 항목 12와 13을보십시오.


여러 사람이 null 검사를 추가하도록 권장했습니다. 때로는 그것이 옳은 일이지만 항상 그런 것은 아닙니다. 건너 뛰어야하는 이유를 보여주는이 훌륭한 기사를 확인하십시오.

http://misko.hevery.com/2009/02/09/to-assert-or-not-to-assert/

참고 URL : https://stackoverflow.com/questions/581873/best-way-to-handle-multiple-constructors-in-java

반응형