Java Swing 클래스는 언제 확장해야합니까?


35

상속 구현에 대한 나의 현재 이해는 IS-A 관계가 존재 하는 경우에만 클래스를 확장해야한다는 것 입니다. 부모 클래스가 다른 기능을 가진 더 구체적인 자식 유형을 가질 수 있지만 부모에서 추상화 된 공통 요소를 공유합니다.

Java 교수가 우리에게 권장하는 것이 있기 때문에 이해하는 것에 의문을 제기하고 있습니다. 그는 JSwing우리가 수업 시간에 구축 하는 응용 프로그램에 대해 권장했습니다

하나는 모든 확장해야 JSwing클래스 ( JFrame, JButton, JTextBox별도의 사용자 정의 클래스로 등) 및 (구성 요소 크기, 구성 요소 라벨 등 같은) 그들에 GUI 관련 사용자 정의를 지정

지금까지는 좋았지 만, 유일한 구별 요소는 레이블이지만 모든 JButton에는 자체 사용자 정의 확장 클래스가 있어야한다고 조언합니다.

예를 들어, GUI에 OkayCancel 버튼이 두 개있는 경우 . 그는 다음과 같이 확장해야한다고 권장합니다.

class OkayButton extends JButton{
    MainUI mui;
    public OkayButton(MainUI mui) {
        setSize(80,60);
        setText("Okay");
        this.mui = mui;
        mui.add(this);        
    }
}

class CancelButton extends JButton{
    MainUI mui;
    public CancelButton(MainUI mui) {
        setSize(80,60);
        setText("Cancel");
        this.mui = mui;
        mui.add(this);        
    }
}

보시다시피 유일한 차이점은 setText기능에 있습니다.

이 표준 관행입니까?

Btw, 이것이 논의 된 과정 을 Java의 Best Programming Practices 라고합니다.

[교수의 답변]

그래서 교수와 문제를 논의하고 답변에 언급 된 모든 요점을 제기했습니다.

그의 서브 정당화는 GUI 설계 표준을 준수하면서 재사용 가능한 코드를 제공한다는 것입니다. 예를 들어 개발자가 한 창에서 사용자 정의 OkayCancel단추를 사용한 경우 다른 창에서도 동일한 단추를 배치하는 것이 더 쉬울 것입니다.

내가 생각하는 이유가 있지만 여전히 상속을 악용 하고 코드를 취약하게 만듭니다.

나중에, 모든 개발자가 실수로 부를 수있는 setTextOkay버튼을 변경합니다. 이 경우 하위 클래스는 성가신 것입니다.


3
클래스 외부에서 동일한 공개 메소드 JButton를 작성 JButton하고 호출 할 수 있는데 왜 생성자에서 공개 메소드를 확장 하고 호출 합니까?

17
가능하면 그 교수에게 가까이 가지 마십시오. 이것은 모범 사례 와는 거리가 멀다 . 그림과 같은 코드 복제는 매우 나쁜 냄새입니다.
njzk2

7
이유를 물어 보면 주저하지 말고 동기를 부여하십시오.
Alex

9
코드가 끔찍하기 때문에 공감하고 싶습니다.하지만 나쁜 교수의 말을 맹목적으로 받아들이지 않고 질문하는 것이 좋기 때문에 공감하고 싶습니다.
Nic Hartley

1
@ 알렉스 나는 교수의 정당성으로 질문을 업데이트했습니다
Paras

답변:


21

a 는 대체 될 수 없기 때문에 Liskov 대체 원칙에 위배 됩니다. 예를 들어, 원하는대로 단추 레이블을 변경할 수 있습니다. 그러나 그렇게하는 것은 내부 불변성을 위반합니다.OkayButtonButtonOkayButton

이것은 코드 재사용을위한 전형적인 상속 오용입니다. 대신 도우미 메소드를 사용하십시오.

이것을하지 않는 또 다른 이유는 이것이 선형 코드와 같은 것을 달성하는 복잡한 방법이기 때문입니다.


OkayButton당신이 생각하고있는 불변성을 가지고 있지 않는 한 이것은 LSP를 위반하지 않습니다 . 는 OkayButton그것은 단지 기본값으로 텍스트를 고려하고 완전히 텍스트 수정을 지원하고자 할 수 있습니다, 여분의 불변을하지 청구 할 수 있습니다. 여전히 좋은 생각은 아니지만 그런 이유로 아닙니다.
hvd

당신이 할 수 있기 때문에 그것을 위반하지 않습니다. 즉, 메소드 activateButton(Button btn)가 버튼을 기대하면 쉽게 인스턴스를 제공 할 수 있습니다 OkayButton. 이 원칙은 상속이 거의 쓸모 없게되므로 정확히 동일한 기능 을 가져야한다는 의미는 아닙니다 .
Sebb

1
@Sebb 그러나 setText(button, "x")(가정 한) 불변을 위반하기 때문에 함수와 함께 사용할 수 없습니다 . 이 특정 OkayButton에 흥미로운 불변이 없을 수도 있으므로 잘못된 예를 선택했다고 생각합니다. 그러나 그럴 수도 있습니다. 예를 들어 클릭 핸들러에 if (myText == "OK") ProcessOK(); else ProcessCancel();? 그런 다음 텍스트는 불변의 일부입니다.
usr

@ usr 나는 텍스트가 불변의 일부라고 생각하지 않았습니다. 네 말이 맞아, 그때 위반이야
Sebb

50

가능한 모든 방법으로 완전히 끔찍합니다. 에서 가장 , JButton의 생산 공장 함수를 사용합니다. 심각한 확장 요구가있는 경우에만 상속해야합니다.


20
+1. 자세한 내용은 AbstractButton의 Swing 클래스 계층 구조를 참조하십시오 . 그런 다음 JToggleButton 의 계층 구조를보십시오 . 상속 은 다른 유형 의 버튼 을 개념화하는 데 사용됩니다 . 그러나 비즈니스 개념을 차별화하는 데 사용되지는 않습니다 ( 예, 아니오, 계속, 취소 ... ).
Laiv

2
@Laiv : 심지어 예를 들어,에 대한 구별 유형을 가진 JButton, JCheckBox, JRadioButton, 등의 유형이 표준 일반 AWT, 같은 다른 툴킷을 잘 알고있는 개발자 단지 양보입니다. 원칙적으로 단일 버튼 클래스로 처리 할 수 ​​있습니다. 실제 차이를 만드는 모델과 UI 델리게이트의 조합입니다.
Holger

예, 하나의 버튼으로 처리 할 수 ​​있습니다. 그러나 실제 계층 구조는 모범 사례로 노출 될 수 있습니다. 새 버튼을 만들거나 이벤트 및 동작을 다른 구성 요소에 위임하려면 환경 설정이 중요합니다. 내가 있다면, 나는 owm 버튼을 모델링하고 그 행동, 행동을 캡슐화하기 위해 Swing 구성 요소를 확장 할 것입니다 ... 프로젝트에서 쉽게 구현하고 후배를 쉽게 만들 수 있습니다. 웹 환경에서는 너무 많은 이벤트 처리보다 웹 구성 요소를 선호합니다. 어쨌든 UI를 Behaivors에서 분리 할 수 ​​있습니다.
Laiv

12

이것은 스윙 코드를 작성하는 매우 비표준적인 방법입니다. 일반적으로 하위 UI 및 이벤트 핸들러를 설정하기 위해 가장 일반적으로 JFrame 인 Swing UI 구성 요소의 서브 클래스 만 작성하는 경우는 거의 없지만 서브 클래 싱은 불필요하며 많은 사람들이 권장하지 않습니다. 버튼 및 기타 텍스트에 대한 사용자 정의 제공은 일반적으로 AbstractAction 클래스 (또는 제공되는 Action 인터페이스 ) 를 확장하여 수행됩니다 . 텍스트, 아이콘 및 기타 필요한 시각적 사용자 정의를 제공하고 실제 코드에 링크 할 수 있습니다. 이것은 보여주는 예제보다 훨씬 나은 UI 코드 작성 방법입니다.

(그런데 구글 학자들은 당신이 인용 한 논문에 대해 들어 본 적이 없습니다 . 더 정확한 참고 자료가 있습니까?)


"표준 방식"이란 정확히 무엇입니까? 모든 구성 요소에 대해 하위 클래스를 작성하는 것은 좋지 않은 생각이지만 공식 Swing 자습서는 여전히이 방법을 선호합니다. 나는 교수가 프레임 워크의 공식 문서에 표시된 스타일을 따르고 있다고 생각합니다.
에서 오는

@ COMEFROM 나는 전체 Swing 튜토리얼을 읽지 않았 음을 인정할 것이므로이 스타일이 사용되는 곳을 놓쳤을 수도 있지만 내가 본 페이지는 기본 클래스를 사용하고 하위 클래스를 구동하지 않는 경향이 있습니다 (예 : docs.oracle) .com / javase / tutorial / uiswing / components / button.html
Jules

@Jules 혼란에 대해 유감스럽게 생각합니다. 교수님이 가르치는 코스 / 논문이 Java의 모범 사례라고합니다.
Paras

1
일반적으로 사실이지만 다른 주요 예외가 JPanel있습니다. JFrame패널은 단지 추상 컨테이너이기 때문에 실제로보다 서브 클래스 화하는 것이 더 유용합니다 .
Ordous

10

IMHO, Java의 Best Programming Practices는 Joshua Bloch의 저서 "Effective Java"에 의해 정의됩니다. 선생님이 OOP 연습을 제공하는 것이 좋으며 다른 사람들의 프로그래밍 스타일을 읽고 쓰는 법을 배우는 것이 중요합니다. 그러나 Josh Bloch의 책을 제외하면 모범 사례에 대한 의견은 매우 다양합니다.

이 클래스를 확장하려는 경우 상속을 활용할 수도 있습니다. MyButton 클래스를 만들어 공통 코드를 관리하고 변수 부분에 대해 하위 클래스를 만듭니다.

class MyButton extends JButton{
    protected final MainUI mui;
    public MyButton(MainUI mui, String text) {
        setSize(80,60);
        setText(text);
        this.mui = mui;
        mui.add(this);        
    }
}

class OkayButton extends MyButton{
    public OkayButton(MainUI mui) {
        super(mui, "Okay");
    }
}

class CancelButton extends MyButton{
    public CancelButton(MainUI mui) {
        super(mui, "Cancel");
    }
}

이것이 언제 좋은 생각입니까? 당신이 만든 타입 을 사용할 때 ! 예를 들어 팝업 창을 만드는 기능이 있고 서명은 다음과 같습니다.

public void showPopUp(String text, JButton ok, JButton cancel)

방금 만든 이러한 유형은 아무런 효과가 없습니다. 그러나:

public void showPopUp(String text, OkButton ok, CancelButton cancel)

이제 유용한 것을 만들었습니다.

  1. 컴파일러는 showPopUp이 OkButton 및 CancelButton을 사용하는지 확인합니다. 코드를 읽는 사람 은이 종류의 문서가 오래된 경우 컴파일 타임 오류가 발생하기 때문에이 함수의 사용법을 알고 있습니다. 이것은 중요한 혜택입니다. 형식 안전성의 이점에 대한 1 ~ 2 개의 경험적 연구에 따르면 인간 코드 이해력이 정량화 할 수있는 유일한 이점이라는 사실이 밝혀졌습니다.

  2. 또한 함수에 전달하는 버튼의 순서를 바꾸는 오류를 방지합니다. 이러한 오류는 파악하기 어렵지만 매우 드물기 때문에 약간의 이점이 있습니다. 이것은 유형 안전을 판매하는 데 사용되었지만 경험적으로 특히 유용한 것으로 입증되지는 않았습니다.

  3. 기능의 첫 번째 형태는 두 개의 버튼을 표시하기 때문에 더 유연합니다. 때로는 어떤 종류의 버튼을 사용할지 제한하는 것보다 낫습니다. 더 많은 상황에서 버튼 기능을 테스트해야한다는 것을 기억하십시오. 기능이 올바른 종류의 버튼을 전달할 때만 작동한다면 어떤 종류의 버튼도 필요하다고 가장하여 어떤 호의를 베풀지 않는 사람은 아무것도하지 않습니다.

객체 지향 프로그래밍의 문제점은 말보다 카트를 놓는 것입니다. 유형을 작성하는 것이 가치가 있는지 시그니처가 무엇인지 알아야 함수를 먼저 작성해야합니다. 그러나 Java에서는 함수 시그니처를 작성할 수 있도록 먼저 유형을 만들어야합니다.

이러한 이유로 Clojure 코드를 작성하여 함수를 먼저 작성하는 것이 얼마나 멋진 지 확인할 수 있습니다. 점근 복잡성에 대해 가능한 많이 생각하면서 빠르게 코딩 할 수 있으며 Clojure의 불변성을 가정하면 Java에서 유형이하는 것처럼 버그를 예방할 수 있습니다.

나는 여전히 대규모 프로젝트에 대한 정적 유형의 팬이며 이제 기능을 먼저 작성하고 나중에 Java에서 유형의 이름 을 지정할 수있는 일부 기능 유틸리티를 사용합니다 . 그냥 생각이야

추신 : 나는 mui포인터를 최종적으로 만들었습니다 . 변경 할 필요는 없습니다.


6
3 점 중 1 개와 3 개는 일반 JButton 및 적절한 입력 매개 변수 명명 체계를 사용하여 처리 할 수 ​​있습니다. 포인트 2는 실제로 코드를 더 밀접하게 결합한다는 단점이 있습니다. 버튼 순서를 바꾸려면 어떻게해야합니까? 확인 / 취소 버튼 대신 예 / 아니요 버튼을 사용하려면 어떻게해야합니까? YesButton과 Nobutton을 사용하는 완전히 새로운 showPopup 메소드를 작성 하시겠습니까? 기본 JButton을 사용하는 경우 유형이 이미 존재하므로 먼저 유형을 만들 필요가 없습니다.
Nzall

@NateKerkhofs가 말한 것을 확장하여 최신 IDE는 실제 표현식을 입력하는 동안 공식 인수 이름을 보여줍니다.
솔로몬 느린

8

교사가 실제로 Swing 구성 요소를 사용하여 확장해야한다고 믿지 않는다고 확신합니다. 나는 그들이 클래스 확장을 연습하도록 강제로 예제로 사용하고 있다고 확신합니다. 나는 실제 모범 사례에 대해 너무 걱정하지 않을 것입니다.

말했다되고 있다는 현실 세계에서 우리가 선호 상속을 통해 조성 .

"IS-A 관계가있는 경우 클래스를 확장해야합니다"라는 규칙이 불완전합니다. 큰 굵은 글씨로 "... 그리고 클래스의 기본 동작을 변경해야합니다"로 끝나야 합니다.

귀하의 예는 해당 기준에 맞지 않습니다. 텍스트를 설정 하기 extend위해 JButton수업 을 할 필요는 없습니다 . 클래스에 구성 요소를 추가 하기 extend위해 JFrame수업 을 할 필요는 없습니다 . 기본 구현을 사용하여 이러한 작업을 제대로 수행 할 수 있으므로 상속을 추가하면 불필요한 복잡성이 추가됩니다.

extends다른 클래스가 있는 클래스를 보면 해당 클래스가 어떻게 바뀌는 지 궁금합니다 . 당신이 아무것도 바꾸지 않는다면, 수업을 전혀 보지 못하게하십시오.

질문으로 돌아 가기 : 언제 Java 클래스를 확장해야합니까? 수업을 확장해야하는 정말, 정말, 좋은 이유가있을 때.

구체적인 예는 다음과 같습니다. 사용자 지정 페인팅을 수행하는 한 가지 방법 (게임이나 애니메이션 또는 사용자 지정 구성 요소의 경우)은 JPanel클래스 를 확장하는 것 입니다. (즉에 대한 자세한 정보를 원하시면 여기 .) 당신은 확장 JPanel이 필요하기 때문에 클래스를 오버라이드 (override)paintComponent() 기능을. 실제로 이렇게 하면 클래스의 동작이 변경 됩니다. 기본 JPanel구현 으로는 사용자 정의 페인팅을 수행 할 수 없습니다 .

그러나 내가 말했듯이, 선생님은 아마도 당신이 수업을 확장하도록 강요하는 변명으로이 예제들을 사용하고있을 것입니다.


1
아 잠깐만, 당신은 Border추가 장식을 그리는 것을 설정할 수 있습니다 . 또는을 만들고 JLabel사용자 지정 Icon구현을 전달 하십시오. JPanel페인팅 을 위해 서브 클래스 를 만들 필요는 없습니다 (그리고 JPanel대신에 왜 JComponent).
Holger

1
나는 당신이 여기에 올바른 아이디어를 가지고 있다고 생각하지만 아마도 더 좋은 예를 선택할 수 있습니다.

1
그러나 나는 당신의 대답이 정확하고 당신이 좋은 지적 을 반복하고 싶다 . 특정 예제와 자습서가 약하게 지원한다고 생각합니다.

1
@KevinWorkman Swing 게임 개발에 대해 잘 모르지만 Swing에서 일부 사용자 정의 UI를 수행했으며 "Swing 클래스를 확장하지 않으므로 구성을 통해 구성 요소와 렌더러를 쉽게 연결할 수 있습니다"라는 표준이 상당히 표준입니다.

1
"나는 그들이 당신을 강제로 예제로 이것을 사용하고 내기 ..."내 주요 동료 중 하나! X를하는 이유에 대한 끔찍하게 정렬 된 예를 사용하여 X를 수행 하는 방법 을 가르치는 강사.
Solomon Slow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.