현실에서 다형성은 어떻게 사용됩니까? [닫은]


17

실제 프로젝트에서 다형성이 어떻게 사용되는지 이해하려고 노력하고 있지만 Animal메소드 가있는 부모 클래스 speak()와이 메소드를 재정의하는 많은 하위 클래스가 있는 고전적인 예 (또는 이와 유사한 것) 만 찾을 수 있습니다. 다음 speak()과 같이 임의의 자식 개체 에서 메서드 를 호출 할 수 있습니다 .

Animal animal;

animal = dog;
animal.speak();

animal = cat;
animal.speak();



1
매일보고보고 사용하는 컬렉션 자체는 다형성이 무엇인지 이해하기에 충분합니다. 그러나 문제 해결에서 다형성을 효과적으로 사용하는 방법은 단순히 토론하는 것이 아니라 경험을 통해 가장 많이 얻는 기술입니다. 손을 더 럽히십시오.
Durgadass S 07-07-17

모든 종류의 최소 인터페이스 (예 : 그려야하는 개체 집합)를 모두 지원하는 형식 집합이있는 경우 인터페이스를 호출 할 때 개체 간의 차이를 숨기려면 일반적으로 인터페이스가 적합합니다. 또한 기본 객체를 서비스 할 수있는 메소드가있는 API를 사용하거나 API에서 상속하는 상당수의 유형을 사용 하는 경우 다형성이 차이점을 추상화하는 가장 좋은 방법 일 수 있습니다 그 유형.
jrh

일반적으로 다른 유형을 처리하기 위해 오버로드 된 메소드를 자주 만들고 코드가 유사하거나 if(x is SomeType) DoSomething()자주 작성하는 경우 다형성을 사용하는 것이 좋습니다. 나에게 다형성은 별도의 방법을 만드는시기와 비슷한 결정입니다. 코드를 몇 번 반복하면 일반적으로 코드를 메서드로 리팩터링하고 if object is this type do this코드를 자주 만드는 경우 인터페이스 또는 클래스를 리팩토링하고 추가 할 가치가 있습니다.
jrh

답변:


35

스트림 은 다형성의 좋은 예입니다.

스트림은 "읽거나 쓸 수있는 바이트 시퀀스"를 나타냅니다. 그러나이 순서는 파일, 메모리 또는 여러 종류의 네트워크 연결에서 나올 수 있습니다. 또는 기존 스트림을 래핑하고 암호화 또는 압축과 같은 방식으로 바이트를 변환하는 데코레이터 역할을 할 수 있습니다.

이런 식으로 Stream을 사용하는 클라이언트는 바이트의 출처를 신경 쓸 필요가 없습니다. 순서대로 읽을 수 있습니다.

일부는 Stream다형성의 잘못된 예 라고 말합니다. 네트워크 스트림은 읽기 또는 쓰기 만 허용하지만 동시에 둘다는 허용하지 않는 것처럼 구현자가 지원하지 않는 많은 "기능"을 정의하기 때문입니다. 또는 추구 부족. 그러나 그것은 Stream독립적으로 구현 될 수있는 많은 부분들로 세분 될 수있는 복잡성의 문제 일 뿐이다 .


2
C와 같은 복수의 가상 상속 언어 ++, 심지어 기본 스트림 클래스의 입력 및 출력 스트림 클래스를 도출하고, 모두는 I / O 스트림을 생성하도록 연장하여 ...은 "무서워 다이아몬드"패턴을 나타낼 수 예
환류

2
@gyre 그리고 다이아몬드 패턴을“두려워”할 이유가 없습니다. 다이아몬드와 반대되는 부분을 알고 이름 충돌을 일으키지 않는 것이 중요하고 도전적이고 성가 시며 실행 가능한 다이아몬드 패턴을 피해야하는 이유는 ...하지만 너무 두려워하지 마십시오. 예를 들어 명명 규칙을 사용하면 문제를 해결할 수 있습니다.
KRyan

+1 Stream은 내가 가장 좋아하는 다형성 예제입니다. 나는 더 이상 결함이있는 '동물, 포유 동물, 개'모델을 사람들에게 가르치려고 시도조차하지 않는다 Stream.
Pharap

@KRyan 나는 그것을 "두려운 다이아몬드"라고 부르면서 내 자신의 생각을 표현하지 않았다. 완전히 동의 해; 모든 개발자가 자신의 머리를 감싸서 적절하게 사용할 수 있어야한다고 생각합니다.
gyre

@gyre 아, 맞아요. 그렇기 때문에 제가“그리고”로 시작한 이유는 그것이 모순이 아니라 여러분의 생각의 확장이라는 것을 나타냅니다.
KRyan

7

일반적인 게임 관련 예제는 또는 Entity같은 일반 멤버를 제공 하는 기본 클래스 입니다.draw()update()

더 순수한 데이터 중심의 예를 들어 기본 클래스가있을 수 Serializable일반적인를 제공 saveToStream()하고 loadFromStream().


6

다양한 종류의 다형성이 있으며, 관심있는 것은 일반적으로 런타임 다형성 / 동적 디스패치입니다.

런타임 다형성에 대한 매우 높은 수준의 설명은 메소드 호출이 인수의 런타임 유형에 따라 다른 작업을 수행한다는 것입니다. 객체 자체는 메소드 호출을 해결해야합니다. 이것은 엄청난 유연성을 허용합니다.

이 유연성을 사용하는 가장 일반적인 방법 중 하나는 종속성 주입입니다 . 예를 들어 다른 구현간에 전환하거나 테스트를 위해 모의 객체를 주입 할 수 있습니다. 가능한 한 제한된 수의 선택 만 할 수 있다는 것을 미리 알고 있다면 조건부로 하드 코딩을 시도 할 수 있습니다.

void foo() {
  if (isTesting) {
    ... // do mock stuff
  } else {
    ... // do normal stuff
  }
}

이로 인해 코드를 따르기가 어렵습니다. 대안은 해당 foo 조작을위한 인터페이스를 도입하고 해당 인터페이스의 일반 구현 및 모의 구현을 작성하고 런타임시 원하는 구현에 "주입"하는 것입니다. "의존성 주입"은 "올바른 대상을 인수로 전달"하는 복잡한 용어입니다.

실제 예를 들어, 저는 현재 친절한 기계 학습 문제를 해결하고 있습니다. 예측 모델이 필요한 알고리즘이 있습니다. 그러나 다른 기계 학습 알고리즘을 시도하고 싶습니다. 그래서 인터페이스를 정의했습니다. 예측 모델에서 무엇이 필요합니까? 일부 입력 샘플, 예측 및 오류가 제공됩니다.

interface Model {
  def predict(sample) -> (prediction: float, std: float);
}

내 알고리즘은 모델을 훈련시키는 팩토리 함수를 사용합니다.

def my_algorithm(..., train_model: (observations) -> Model, ...) {
  ...
  Model model = train_model(observations);
  ...
  y, std = model.predict(x)
  ...
}

이제 모델 인터페이스를 다양하게 구현했으며 서로 벤치마킹 할 수 있습니다. 이러한 구현 중 하나는 실제로 두 개의 다른 모델을 가져 와서 확장 된 모델로 결합합니다. 이 인터페이스 덕분에

  • 내 알고리즘은 특정 모델에 대해 미리 알 필요가 없습니다.
  • 모델을 쉽게 교체 할 수 있고
  • 모델을 구현할 때 많은 유연성이 있습니다.

다형성의 전형적인 유스 케이스는 GUI에 있습니다. Java AWT / Swing /…과 같은 GUI 프레임 워크에는 다른 구성 요소가 있습니다. 구성 요소 인터페이스 / 기본 클래스는 화면 자체에 페인팅하거나 마우스 클릭에 반응하는 등의 작업을 설명합니다. 많은 구성 요소는 하위 구성 요소를 관리하는 컨테이너입니다. 그러한 컨테이너는 어떻게 스스로 그릴 수 있습니까?

void paint(Graphics g) {
  super.paint(g);
  for (Component child : this.subComponents)
    child.paint(g);
}

여기서 컨테이너는 하위 구성 요소의 정확한 유형에 대해 미리 알 필요가 없습니다. 컨테이너가 Component인터페이스 를 준수하기 만하면 컨테이너가 단순히 다형성 paint()메서드 를 호출 할 수 있습니다 . 이를 통해 임의의 새로운 구성 요소로 AWT 클래스 계층 구조를 자유롭게 확장 할 수 있습니다.

소프트웨어 개발 전반에 걸쳐 다형성을 기법으로 적용하여 해결할 수있는 많은 반복적 인 문제가 있습니다. 이러한 반복되는 문제 해결 쌍을 디자인 패턴 이라고 하며 그 중 일부는 같은 이름의 책에 수집됩니다. 이 책의 용어에서 필자의 주입 된 머신 러닝 모델은 “알고리즘 제품군을 정의하고 각 알고리즘을 캡슐화하여 상호 교환 가능하게 만드는” 전략 입니다. 컴포넌트가 서브 컴포넌트를 포함 할 수있는 Java-AWT 예제는 컴포지트 의 예제입니다 .

그러나 모든 디자인이 다형성을 사용할 필요는 없습니다 (단위 테스트를위한 의존성 주입을 가능하게하는 것 이상). 그렇지 않으면 대부분의 문제는 매우 정적 인 것입니다. 결과적으로 클래스와 메소드는 종종 다형성에 사용되지 않고 편리한 네임 스페이스와 예쁜 메소드 호출 구문에 사용됩니다. 예를 들어 많은 개발자들은 account.getBalance()거의 동등한 함수 호출보다 메소드 호출을 선호합니다 Account_getBalance(account). 그것은 완벽하게 훌륭한 접근법이며, 많은“방법”호출은 다형성과 관련이 없습니다.


6

대부분의 UI 툴킷에는 많은 상속과 다형성이 있습니다.

예를 들어, 자바 FX UI 툴킷, Button상속의 ButtonBase어느 행 상속 Labeled되는 상속에서 Control어떤 행 상속 Region되는 상속에서 Parent어떤 행 상속 Node되는 상속에서 Object. 많은 레이어가 이전 레이어의 일부 메서드보다 우선합니다.

해당 버튼을 화면에 표시하려면 버튼을에 추가 하면 자식으로 Pane상속 된 모든 항목을 수락 할 수 있습니다 Node. 그러나 창은 일반적인 노드 객체로 볼 때 버튼으로 무엇을 해야하는지 어떻게 알 수 있습니까? 그 물건은 무엇이든 될 수 있습니다. 버튼은 버튼 특정 로직으로 Node의 메소드를 재정의하기 때문에 그렇게 할 수 있습니다. 창은 Node에 정의 된 메소드를 호출하고 나머지는 오브젝트 자체에 남겨 둡니다. 이것은 적용된 다형성의 완벽한 예입니다.

UI 툴킷은 실제 세계의 중요성이 매우 높기 때문에 학문적 및 실용적인 이유로 가르치는 데 유용합니다.

그러나 UI는 큰 단점이 툴킷 : 그들은 경향이 거대한 . 네오 피트 소프트웨어 엔지니어가 공통 UI 프레임 워크의 내부 작업을 이해하려고 할 때, 종종 100 개가 넘는 클래스를 만나게 되며 , 대부분 클래스 는 매우 난해한 목적으로 사용됩니다. "도대체가 무엇입니까 ReadOnlyJavaBeanLongPropertyBuilder? 중요한가? 내가 가지고 무엇 그것의 좋은 이해?" 그 토끼 구멍에서 초보자는 쉽게 길을 잃을 수 있습니다. 그래서 그들은 테러로 도망 치거나 구문을 배우고 표면에서 실제로 일어나고있는 일에 대해 너무 열심히 생각하지 않을 수 있습니다.


3

여기에 이미 좋은 예가 있지만 또 다른 예는 동물을 장치로 바꾸는 것입니다.

  • Device할 수있다 powerOn(), powerOff(), setSleep()할 수 있습니다 getSerialNumber().
  • SensorDevice이 모든 작업을 수행하고 다음과 같은 다형성 기능을 제공 할 수 있습니다 getMeasuredDimension(), getMeasure(), alertAt(threashhold)autoTest().
  • 물론 getMeasure()온도 센서, 광 검출기, 음향 검출기 또는 체적 센서에 대해 동일한 방식으로 구현되지는 않을 것이다. 물론 이러한보다 전문화 된 각 센서에는 몇 가지 추가 기능이 제공 될 수 있습니다.

2

프레젠테이션은 매우 일반적인 응용 프로그램이며 아마도 가장 일반적인 응용 프로그램은 ToString ()입니다. 기본적으로 Animal.Speak ()입니다. 객체에 자신을 표시하도록 지시합니다.

더 일반적으로 말하면 객체에게 "그 일을하도록"지시합니다. Save, Load, Initialize, Dispose, ProcessData, GetStatus를 생각하십시오.


2

다형성의 첫 번째 실제 사용법은 Java의 힙 구현이었습니다.

max와 min 힙의 차이점은 메소드 비교 작업 방식 일뿐입니다.

abstract class Heap {  

 abstract boolean compare ( int x , int y );

 boolean insert(int x ) { ... }

 int removeTop() { ... }
}

그래서 MaxHeap과 MinHeap을 원할 때 상속을 사용할 수있었습니다.

class MaxHeap extends Heap {

   MaxHeap(int maxSize) {super(maxSize);}

   @Override
   boolean compare(int x, int y) {
       return x>y; // x<y for minHeap
   }
}

1

다음은 웹앱 / 데이터베이스 테이블 다형성에 대한 실제 시나리오입니다 .

Ruby on Rails를 사용하여 웹 앱을 개발하고 많은 프로젝트에서 공통적으로 사용하는 한 가지는 파일 (사진, PDF 등)을 업로드하는 기능입니다. 예를 들어, User프로필 사진이 여러 개 Product있을 수도 있고 제품 이미지가 여러 개있을 수도 있습니다. 모두 업로드 및 이미지를 저장의 행동뿐만 아니라 DRY을 유지하고 대한 동작을 공유하기 위해 등, 썸네일을 생성, 크기 조정이 Picture우리가 만들고 싶어, Picture이 모두에 속할 수 다형성 너무 UserProduct.

Rails에서는 다음과 같이 모델을 설계합니다.

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class User < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

pictures테이블 을 작성하기위한 데이터베이스 마이그레이션 :

class CreatePictures < ActiveRecord::Migration[5.0]
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end

    add_index :pictures, [:imageable_type, :imageable_id]
  end
end

imageable_id와는 imageable_type내부적으로 레일에 의해 사용됩니다. 기본적으로 imageable_type클래스 (이름 보유 "User", "Product"등) 및 imageable_id관련 레코드의 ID입니다. 따라서 imageable_type = "User"와 함께 테이블 imageable_id = 1의 레코드가 users됩니다 id = 1.

이를 통해 user.pictures사용자의 사진에 액세스하고 제품의 사진 product.pictures을 얻는 등의 작업 을 수행 할 수 있습니다. 그런 다음 모든 사진 관련 동작이 Photo클래스에 캡슐화되고 (사진이 필요한 각 모델마다 별도의 클래스가 아님) 사물은 건조 상태로 유지됩니다.

더 읽기 : Rails 다형성 연관성 .


0

버블 정렬, 삽입 정렬, 빠른 정렬, 힙 정렬 등과 ​​같은 다양한 정렬 알고리즘이 있으며 복잡도는 다르며 사용하기에 가장 적합한 정렬 알고리즘은 다양한 요인 (예 : 배열의 크기)에 따라 다릅니다.

정렬 인터페이스가 제공되는 클라이언트는 배열을 입력으로 제공 한 다음 정렬 된 배열을 수신하는 데 관심이 있습니다. 특정 요소에 따라 런타임 동안 적절한 정렬 구현을 사용할 수 있습니다. 이것은 다형성이 사용되는 실제 사례 중 하나입니다.

위에서 설명한 것은 런타임 다형성의 예이지만 메서드 오버로드는 컴파일 시간 다형성의 예이며, i / p 및 o / p 매개 변수 유형 및 매개 변수 수에 따라 complier가 적절한 시간에 올바른 방법으로 호출자를 바인딩합니다.

이것이 분명히되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.