태그 보관물: code-smell

code-smell

“일반 데이터”클래스를 사용해야하는 이유가 있습니까? maybe a constructor } OO에 대한 이해는 클래스는 데이터의

레거시 코드에서 때때로 데이터 래퍼에 지나지 않는 클래스를 볼 수 있습니다. 같은 :

class Bottle {
   int height;
   int diameter;
   Cap capType;

   getters/setters, maybe a constructor
}

OO에 대한 이해는 클래스는 데이터의 구조 와 해당 데이터에서 작동하는 방법이라는 것입니다. 이것은이 유형의 객체를 배제하는 것처럼 보입니다. 나에게 그들은 structsOO의 목적을 넘어서는 것이 아닙니다 . 코드 냄새 일 수는 있지만 반드시 악하다고 생각하지는 않습니다.

그러한 물건이 필요한 경우가 있습니까? 이것이 자주 사용된다면, 디자인을 의심하게 만드는가?



답변

분명히 악하지 않고 코드도 냄새가 없습니다. 데이터 컨테이너는 유효한 OO 시민입니다. 때로는 관련 정보를 함께 캡슐화하려고합니다. 다음과 같은 방법을 사용하는 것이 훨씬 좋습니다

public void DoStuffWithBottle(Bottle b)
{
    // do something that doesn't modify Bottle, so the method doesn't belong
    // on that class
}

…보다

public void DoStuffWithBottle(int bottleHeight, int bottleDiameter, Cap capType)
{
}

클래스를 사용하면 DoStuffWithBottle의 모든 호출자를 수정하지 않고도 Bottle에 추가 매개 변수를 추가 할 수 있습니다. 또한 Bottle을 서브 클래 싱하고 필요한 경우 코드의 가독성과 구성을 더욱 향상시킬 수 있습니다.

예를 들어 데이터베이스 쿼리의 결과로 반환 될 수있는 일반 데이터 개체도 있습니다. 이 경우 이들의 용어는 “데이터 전송 개체”라고 생각합니다.

일부 언어에서는 다른 고려 사항도 있습니다. 예를 들어 C # 클래스와 구조체는 다르게 동작합니다. 구조체는 값 형식이고 클래스는 참조 형식이기 때문입니다.


답변

데이터 클래스는 경우에 따라 유효합니다. DTO는 Anna Lear가 언급 한 좋은 예입니다. 그러나 일반적으로 메소드가 아직 발아되지 않은 클래스의 씨앗으로 간주해야합니다. 오래된 코드에서 많은 코드를 사용하는 경우 강력한 코드 냄새로 취급하십시오. 그것들은 종종 OO 프로그래밍으로 전환 한 적이없고 절차 적 프로그래밍의 표시 인 오래된 C / C ++ 프로그래머들에 의해 종종 사용됩니다. 게터와 세터에 항상 의존하는 경우 (또는 개인이 아닌 개인이 직접 액세스하는 경우)는 알기 전에 문제를 일으킬 수 있습니다. 의 정보가 필요한 외부 메소드의 예를 고려하십시오 Bottle.

다음 Bottle은 데이터 클래스입니다.

void selectShippingContainer(Bottle bottle) {
    if (bottle.getDiameter() > MAX_DIMENSION || bottle.getHeight() > MAX_DIMENSION ||
            bottle.getCapType() == Cap.FANCY_CAP ) {
        shippingContainer = WOODEN_CRATE;
    } else {
        shippingContainer = CARDBOARD_BOX;
    }
}

여기에 우리는 Bottle몇 가지 행동을 주었습니다 .)

void selectShippingContainer(Bottle bottle) {
    if (bottle.isBiggerThan(MAX_DIMENSION) || bottle.isFragile()) {
        shippingContainer = WOODEN_CRATE;
    } else {
        shippingContainer = CARDBOARD_BOX;
    }
}

첫 번째 방법은 Tell-Don’t-Ask 원칙에 위배되며, Bottle바보 를 유지함으로써 병에 대한 암묵적인 지식 (예 : 하나의 fragle ( Cap)을 Bottle클래스 외부의 논리에 빠지게 함)을 알 수 있습니다. 습관적으로 게터에게 의지 할 때 이런 종류의 ‘누설’을 막으려면 발가락에 있어야합니다.

두 번째 방법은 Bottle작업을 수행하는 데 필요한 사항 만 묻고 , Bottle깨지기 쉬운 지 또는 주어진 크기보다 큰지를 결정합니다. 결과적으로 메소드와 병 구현 사이의 연결이 훨씬 느슨해집니다. 유쾌한 부작용은이 방법이 더 깨끗하고 표현력이 뛰어나다는 것입니다.

이러한 필드와 함께 클래스에 상주해야 할 논리를 쓰지 않고 객체의 많은 필드를 사용하는 경우는 거의 없습니다. 해당 논리가 무엇인지 파악한 다음 논리를 해당 위치로 이동하십시오.


답변

이것이 당신이 필요로하는 종류라면 괜찮습니다.하지만 제발 제발 제발

public class Bottle {
    public int height;
    public int diameter;
    public Cap capType;

    public Bottle(int height, int diameter, Cap capType) {
        this.height = height;
        this.diameter = diameter;
        this.capType = capType;
    }
}

같은 대신에

public class Bottle {
    private int height;
    private int diameter;
    private Cap capType;

    public Bottle(int height, int diameter, Cap capType) {
        this.height = height;
        this.diameter = diameter;
        this.capType = capType;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getDiameter() {
        return diameter;
    }

    public void setDiameter(int diameter) {
        this.diameter = diameter;
    }

    public Cap getCapType() {
        return capType;
    }

    public void setCapType(Cap capType) {
        this.capType = capType;
    }
}

부디.


답변

@Anna가 말했듯이 분명히 악하지는 않습니다. 물론 작업 (메소드)을 클래스에 넣을 수는 있지만 원하는 경우에만 가능합니다 . 당신은하지 않습니다 에.

클래스가 추상화라는 아이디어와 함께 클래스에 연산을 넣어야한다는 아이디어에 대해 조금 이해하십시오. 실제로 이것은 프로그래머가

  1. 필요한 것보다 많은 클래스를 만듭니다 (중복 데이터 구조). 데이터 구조에 최소한 필요한 것보다 많은 구성 요소가 포함되어 있으면 정규화되지 않으므로 상태가 일치하지 않습니다. 즉, 변경 될 때 일관성을 유지하려면 둘 이상의 위치에서 변경해야합니다. 모든 조정 된 변경을 수행하지 않으면 일관성이없고 버그입니다.

  2. 파트 A가 수정되면 파트 B 및 C에 필요한 변경 사항을 전파하려고 시도 할 수 있도록 통지 메소드를 삽입 하여 문제점 1을 해결하십시오 . 이것이 get-and-set 액세서 메소드를 갖는 것이 권장되는 주요 이유입니다. 이 방법을 사용하는 것이 권장되므로 문제 1을 실례로하여 문제 1과 해결 방법 2를 더 많이 발생시키는 것으로 보입니다. 이로 인해 알림이 불완전하게 구현되어 버그가 발생할뿐만 아니라 런 어웨이 알림의 성능 저하 문제가 발생합니다. 이것들은 무한한 계산이 아니라 매우 긴 계산입니다.

이러한 개념은 일반적으로 이러한 문제로 가득 찬 수백만 라인의 몬스터 응용 프로그램 내에서 일할 필요가 없었던 교사가 좋은 것으로 가르칩니다.

내가하려고하는 것은 다음과 같습니다.

  1. 데이터가 변경 될 때 일관성이없는 상태에 들어갈 가능성을 최소화하기 위해 가능한 한 적은 코드 포인트에서 데이터가 수행되도록 데이터를 가능한 한 정규화 상태로 유지하십시오.

  2. 데이터를 정규화하지 않아야하고 중복성을 피할 수없는 경우 일관성을 유지하기 위해 알림을 사용하지 마십시오. 오히려 일시적인 불일치를 용납하십시오. 이를 수행하는 프로세스에 의해 데이터를 주기적으로 스윕하여 불일치를 해결하십시오. 이렇게하면 알림을 받기 쉬운 성능 및 정확성 문제를 피하면서 일관성을 유지해야 할 책임이 집중됩니다. 그 결과 코드가 훨씬 작고 오류가 없으며 효율적입니다.


답변

이러한 종류의 클래스는 다음과 같은 이유로 중간 규모 / 큰 응용 프로그램을 처리 할 때 매우 유용합니다.

  • 일부 테스트 사례를 작성하고 데이터가 일관성을 유지하는 것은 매우 쉽습니다.
  • 해당 정보와 관련된 모든 종류의 동작을 보유하므로 데이터 버그 추적 시간이 단축됩니다.
  • 그것들을 사용하면 메소드 인수를 가볍게 유지해야합니다.
  • ORM을 사용할 때이 클래스는 유연성과 일관성을 제공합니다. 이미 클래스에있는 간단한 정보를 기반으로 계산 된 복잡한 속성을 추가하면 간단한 방법 하나를 작성하는 것이 좋습니다. 이는 데이터베이스를 확인하고 모든 데이터베이스가 새로운 수정 사항으로 패치되는지 확인해야하는 매우 민첩하고 생산적입니다.

요약하면, 내 경험상 그들은 일반적으로 성가신 것보다 더 유용합니다.


답변

게임 디자인을 사용하면 1000의 함수 호출 오버 헤드와 이벤트 리스너는 때때로 데이터 만 저장하는 클래스를 보유하고 로직을 수행하기 위해 모든 데이터 만 클래스를 반복하는 다른 클래스를 가질 수 있습니다.


답변

안나 리어와

분명히 악하지 않고 코드도 냄새가 없습니다. 데이터 컨테이너는 유효한 OO 시민입니다. 때로는 관련 정보를 함께 캡슐화하려고합니다. 다음과 같은 방법을 사용하는 것이 훨씬 좋습니다.

때때로 사람들은 이런 종류의 프로그래밍이 완벽하다는 것을 명백하게하는 1999 Java 코딩 규칙을 읽는 것을 잊어 버립니다. 실제로 피하지 않으면 코드에서 냄새가납니다! (너무 많은 게터 / 세터)

Java Code Conventions 1999 :
적절한 퍼블릭 인스턴스 변수의 한 예는 클래스가 기본적으로 데이터 구조이며 동작이없는 경우입니다. 다시 말해, 클래스 대신 구조체를 사용했다면 (Java가 구조체를 지원하는 경우), 클래스의 인스턴스 변수를 공개하는 것이 적절합니다.
http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

올바르게 사용하면 POJ (일반적인 데이터 구조)가 POJO보다 낫습니다. POJO가 EJB보다 낫습니다.
http://en.wikipedia.org/wiki/Plain_Old_Data_Structures