생성자를 어떻게 분해 할 수 있습니까?


21

Enemy 클래스가 있다고 가정하면 생성자는 다음과 같습니다.

public Enemy(String name, float width, float height, Vector2 position, 
             float speed, int maxHp, int attackDamage, int defense... etc.){}

생성자가 너무 많은 매개 변수를 가지고 있기 때문에 이것은 좋지 않지만 Enemy 인스턴스를 만들 때 이러한 모든 것을 지정해야합니다. 또한 Enemy 클래스에서 이러한 속성을 원하므로 속성 목록을 반복하고 이러한 매개 변수를 가져 오거나 설정할 수 있습니다. maxHp 및 기타 특정 속성을 하드 코딩하는 동안 EnemyB, EnemyA로 EnemyB를 서브 클래스로 생각하고 있었지만 Enemy 목록 (EnemyA, EnemyB 및 적의).

깨끗하게 코딩하는 방법을 배우려고합니다. 차이가 있다면 Java / C ++ / C #에서 일합니다. 올바른 방향으로 어떤 점이라도 감사합니다.


5
모든 속성을 바인딩하는 하나의 생성자를 갖는 것은 나쁘지 않습니다. 실제로 일부 지속성 환경에서는 필수입니다. 아무 것도 당신은 조각 생성을 한 후에 호출 할 유효성 검사 방법을 가진 여러 생성자를 가질 수 없다고 말합니다.
BobDalgleish

1
리터럴을 사용하여 코드에서 Enemy 객체를 생성하려는 경우 의문의 여지가 있습니다. 그렇지 않으면, 왜 그런지 모르겠지만 데이터베이스 인터페이스 또는 직렬화 문자열에서 데이터를 가져 오는 생성자를 작성하십시오.
Zan Lynx


답변:


58

해결책은 매개 변수를 복합 유형으로 묶는 것입니다. 너비와 높이는 개념적으로 관련이 있습니다. 적의 크기를 지정하며 보통 함께 필요합니다. 그것들은 Dimensions타입 또는 Rectangle위치를 포함 하는 타입 으로 대체 될 수 있습니다 . 반면 , 특히 가속이 나중에 그림에 들어가는 경우 그룹화 position하고 유형 speed으로 만드는 것이 더 합리적 일 수 있습니다 MovementData. 나는 가정 맥락에서 maxHp, attackDamage, defense, 등 또한 함께 속해 Stats유형입니다. 따라서 수정 된 서명은 다음과 같습니다.

public Enemy(String name, Dimensions dimensions, MovementData movementData, Stats stats)

선을 그리는 위치에 대한 자세한 내용은 나머지 코드와 함께 사용되는 데이터에 따라 다릅니다.


21
또한 값이 너무 많으면 단일 책임 원칙을 위반했을 수 있습니다. 그리고 값을 특정 객체로 그룹화하는 것이 이러한 책임을 분리하는 첫 단계입니다.
Euphoric

2
값 목록이 SRP 문제라고 생각하지 않습니다. 대부분은 기본 클래스 생성자를위한 것입니다. 계층의 각 클래스는 단일 책임을 가질 수 있습니다. Enemy를 목표로하는 클래스 Player이지만 일반적인 기본 클래스 Combatant에는 전투 통계가 필요합니다.
MSalters

@MSalters 반드시 SRP 문제를 나타내는 것은 아니지만 가능합니다. 그는 그들이 정적 / 무료 기능을해야 할 때 (그가 사용하는 경우 충분한 수-재정, 그 기능은 적 클래스에 그들의 방법을 찾을 수 할 필요가있는 경우 Dimensions/ MovementData일반 오래된 데이터 컨테이너로) 또는 방법 그는 추상적 인 데이터로 만들어 버리는 경우 ( 유형 / 객체). 예를 들어, 아직 Vector2유형을 작성하지 않은 경우 에서에서 벡터 수학을 수행했을 수 있습니다 Enemy.
Doval

24

Builder 패턴을 살펴볼 수 있습니다 . 링크에서 (패턴 대 대안의 예와 함께) :

[The] Builder 패턴은 생성자 또는 정적 팩토리가 소수 이상의 매개 변수를 갖는 클래스를 디자인 할 때 특히 적합합니다. 특히 이러한 매개 변수의 대부분이 선택적인 경우입니다. 클라이언트 코드는 전통적인 텔레 스코핑 생성자 패턴보다 빌더로 읽고 쓰는 것이 훨씬 쉽고 빌더는 JavaBeans보다 훨씬 안전합니다.


4
짧은 코드 스 니펫이 도움이 될 것입니다. 이것은 다양한 입력으로 복잡한 객체 또는 구조를 구축하기에 좋은 패턴입니다. 다양한 공유 속성을 캡슐화하는 EnemyABuilder, EnemyBBuilder 등의 빌더를 전문화 할 수도 있습니다. 이것은 팩토리 패턴의 반대면이지만 (아래 답변 됨) 개인적으로 선호하는 것은 Builder입니다.
Rob

1
고맙게도 빌더 패턴과 팩토리 패턴은 모두 내가하려고하는 것과 잘 작동하는 것처럼 보입니다. 나는 Builder / Factory와 Doval의 제안이 내가 찾고있는 것이라고 생각합니다. 편집 : 나는 하나의 답변 만 표시 할 수 있다고 생각합니다. 나는 주제 질문에 대답하기 때문에 Doval에게 줄 것이지만 다른 것들은 내 특정 문제에 똑같이 도움이됩니다. 모두 감사합니다.
Travis

언어가 팬텀 유형을 지원하는 경우 일부 / 모든 SetX 함수가 호출되도록하는 빌더 패턴을 작성할 수 있습니다. 또한 원하는 경우 한 번만 호출되도록 할 수도 있습니다.
Thomas Eding

1
@ Mark16 링크에서 언급 한 것처럼 > Builder 패턴은 Ada 및 Python에서 볼 수있는 명명 된 선택적 매개 변수를 시뮬레이션합니다. 질문에 C #도 사용한다고 언급했으며 해당 언어 는 명명 된 / 선택적 인수 (C # 4.0 기준)를 지원하므로 다른 옵션 일 수 있습니다.
Bob

5

서브 클래스를 사용하여 일부 값을 사전 설정하는 것은 바람직하지 않습니다. 새로운 유형의 적이 다른 행동이나 새로운 속성을 가진 경우에만 서브 클래스.

공장 패턴은 일반적으로 사용되는 정확한 클래스를 통해 추상적으로 사용되지만, 또한 객체 생성을위한 템플릿을 제공하는 데 사용할 수 있습니다 :

class EnemyFactory {

    // each of these methods is essentially a template for a kind of enemy

    Enemy enemyA(String name, ...) {
        return new Enemy(name, ..., presetValue, ...);
    }

    Enemy enemyB(String name, ...) {
        return new Enemy(name, ..., otherValue, ...);
    }

    Enemy enemyC(String name, ...) {
        return new EnemySubclass(name, ..., otherValue, ...);
    }

    ...
}

EnemyFactory factory = new EnemyFactory();
Enemy a = factory.enemyA("fred", ...);
Enemy b = factory.enemyB("willy", ...);

0

나는 당신이 독립적으로 사용할 객체를 나타내는 클래스에 서브 클래스를 예약합니다. 높이, 위치.

많은 입력 매개 변수가있는 생성자에는 본질적으로 잘못된 것이 보이지 않지만 조금 나누려면 대부분의 매개 변수를 설정하는 하나의 생성자와 사용할 수있는 다른 (오버로드) 생성자를 가질 수 있습니다 특정 값을 설정하고 다른 값을 기본값으로 설정합니다.

사용하기로 선택한 언어에 따라 일부는 생성자의 입력 매개 변수에 대한 기본값을 다음과 같이 설정할 수 있습니다.

Enemy(float height = 42, float width = 42);

0

Rory Hunter의 답변에 추가하는 코드 예제 (Java) :

public class Enemy{
   private String name;
   private float width;
   ...

   public static class Builder{
       private Enemy instance;

       public Builder(){
           this.instance = new Enemy();
       }


       public Builder withName(String name){
           instance.name = name;
           return this;
       }

       ...

       public Enemy build(){
           return instance;
       }
   }
}

이제 다음과 같이 Enemy의 새 인스턴스를 만들 수 있습니다.

Enemy myEnemy = new Enemy.Builder().withName("John").withX(x).build();

1
프로그래머는 둘러보기 개념적인 질문이며 답변 을 통해 설명 할 있습니다. 설명 대신 코드 덤프를 던지는 것은 IDE에서 화이트 보드로 코드를 복사하는 것과 같습니다. 친숙해 보이고 때로는 이해할 수 있지만 이상하게 느껴집니다. 화이트 보드에는 컴파일러가 없습니다
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.