같은 클래스에서 생성 된 객체가 고유 한 메소드 정의를 가질 수 있습니까?


14

같은 클래스를 공유하는 둘 이상의 객체의 포인트가 동작이 동일하다는 것, 즉 메소드가 동일하기 때문에 이것이 이상한 질문처럼 보입니다.

그러나 필드에 다른 값을 할당 할 수있는 것과 같은 방식으로 객체의 메소드를 재정의 할 수있는 OOP 언어가 있는지 궁금합니다. 결과적으로 동일한 클래스로 구성된 객체는 더 이상 정확히 동일한 동작을 나타내지 않습니다.

내가 실수하지 않으면이 JavaScript를 할 수 있습니까? 이 질문과 함께 왜 누군가가 이것을 원할까요?


9
기술 용어는 "시제품 기반"입니다. 검색하면 OOP의이 맛에 대해 많은 자료를 얻을 수 있습니다. (많은 사람들은 그러한 구조가 적절한 "클래스"를 고려하지 않는다는 것을 명심하십시오. 정확하게는 그들이 균일 한 속성과 방법을 갖지 않기 때문입니다.)
Kilian Foth

1
각기 다른 버튼 처리기에 연결되어 있기 때문에 클릭 할 때 버튼의 두 인스턴스가 어떻게 다른 작업을 수행 할 수 있습니까? 물론, 항상 같은 일을한다고 주장 할 수 있습니다. 버튼 핸들러라고 부릅니다. 어쨌든 당신의 언어가 델리게이트 또는 함수 포인터를 지원한다면 이것은 쉽다-델리게이트 또는 함수 포인터를 가지고있는 프로퍼티 / 필드 / 멤버 변수가 있고, 메소드의 구현은 당신이 가거나 멀리 갈 수 있습니다.
Kate Gregory

2
복잡합니다 :) 슬링 함수 참조가 일반적인 언어에서 일부 코드를 setClickHandler()메서드 에 전달 하고 동일한 클래스의 다른 인스턴스가 매우 다른 작업을 수행하는 것은 쉽습니다 . 편리한 람다식이없는 언어에서는 새 핸들러에 대해서만 새로운 익명 하위 클래스를 만드는 것이 더 쉽습니다. 전통적으로 재정의 메소드는 새로운 클래스 의 특징으로 간주 되었지만 속성 값을 설정하는 것은 아니지만 특히 핸들러 함수를 사용하면 두 가지가 매우 유사한 효과를 가지므로 구별은 단어와의 전쟁이됩니다.
Kilian Foth

1
정확히 당신이 요구하는 것은 아니지만 이것은 전략 디자인 패턴처럼 들립니다. 여기에는 런타임에 유형을 효과적으로 변경하는 클래스의 인스턴스가 있습니다. 이것은 언어를 허용하지는 않지만 "누군가 이것을 원할까요?"라고 말했기 때문에 언급 할만한 가치가 있기 때문에 주제에서 벗어난 것입니다.
tony

@Killian Forth 프로토 타입 기반 OOP에는 정의에 따라 클래스가 없으므로 OP가 요구하는 것과 정확히 일치하지 않습니다. 중요한 차이점은 클래스가 인스턴스가 아니라는 것입니다.
eques

답변:


9

대부분의 (클래스 기반) OOP 언어의 메소드는 유형별로 고정되어 있습니다.

JavaScript는 클래스 기반이 아닌 프로토 타입 기반이므로 "클래스"와 객체간에 뚜렷한 구분이 없기 때문에 인스턴스 별 기반에서 메소드를 대체 할 수 있습니다. 실제로 JavaScript의 "클래스"는 인스턴스의 작동 방식에 대한 템플릿과 같은 객체입니다.

스칼라, 자바 8, C # (대리자를 통한) 등의 일급 함수를 허용하는 모든 언어는 인스턴스 별 메서드 재정의와 같이 작동 할 수 있습니다. 함수 유형으로 필드를 정의한 다음 각 인스턴스에서이를 대체해야합니다.

스칼라는 또 다른 가능성이 있습니다. 스칼라에서는 클래스 키워드 대신 객체 키워드를 사용하여 객체 싱글 톤을 만들 수 있으므로 클래스를 확장하고 메서드를 재정 의하여 해당 기본 클래스의 새 인스턴스를 재정의 할 수 있습니다.

누군가 왜 이것을할까요? 여러 가지 이유가있을 수 있습니다. 다양한 필드 조합을 사용하는 것보다 동작을 더 엄격하게 정의해야 할 수도 있습니다. 또한 코드를 분리하고 체계적으로 유지할 수 있습니다. 그러나 일반적으로 이러한 경우는 드물고 필드 값을 사용하는보다 간단한 솔루션이 종종 있다고 생각합니다.


첫 문장은 말이되지 않습니다. 클래스 기반 OOP는 고정 유형과 어떤 관련이 있습니까?
Bergi

즉, 유형마다 메소드 세트 및 정의가 고정되어 있거나 다른 말로 메소드를 추가하거나 변경하려면 새 유형을 작성해야합니다 (예 : 서브 타이핑).
eques

@Bergi : 클래스 기반 OOP에서 단어 "class"와 "type"은 본질적으로 동일한 것을 의미합니다. 타입 정수가 "hello"값을 갖도록하는 것은 이치에 맞지 않기 때문에 Employee 클래스에 속하지 않는 값이나 메소드를 갖도록 Employee 타입을 갖는 것도 의미가 없습니다.
slebetman

@ slebetman : 클래스 기반 OOP 언어가 반드시 정적으로 강제로 타이핑하는 것은 아닙니다.
Bergi

@ Bergi : 위의 답변에서 "most"의 의미입니다 (대부분 모두를 의미하는 것은 아닙니다). 나는 단지 당신의 "무엇을해야하는지"에 대해 논평하고 있습니다.
slebetman

6

귀하의 질문에 대한 동기 부여를 추측하기 어렵 기 때문에 가능한 답변이 귀하의 실제 관심사를 다루거나 다루지 않을 수 있습니다.

프로토 타입이 아닌 일부 언어에서도이 효과를 근사화 할 수 있습니다.

예를 들어 Java에서 익명의 내부 클래스는 설명하고있는 것과 매우 비슷합니다. 원본의 하위 클래스를 만들고 인스턴스화하여 원하는 메서드 나 메서드 만 재정의 할 수 있습니다. 결과 클래스는 instanceof원래 클래스이지만 동일한 클래스 는 아닙니다 .

왜 이것을하고 싶습니까? Java 8 람다 식을 사용하면 최고의 사용 사례가 많이 사라진다고 생각합니다. 적어도 이전 버전의 Java에서는 사소하고 좁은 사용 클래스의 확산을 피할 수 있습니다. 즉, 작은 기능적인 방식 만 다른 많은 관련 사용 사례가있는 경우 필요할 때 동작 차이를 주입하여 거의 즉각적으로 (거의) 생성 할 수 있습니다.

즉, J8 이전 버전조차도 차이를 필드 또는 3으로 이동하고 생성자에 주입하기 위해 종종 리팩터링 될 수 있습니다. 물론 J8을 사용하면 메서드 자체를 클래스에 주입 할 수 있지만 다른 리팩토링이 더 깔끔 할 때 (그렇지 않은 경우) 유혹이있을 수 있습니다.


6

인스턴스 별 방법을 제공하는 언어를 요청했습니다. 이미 Javascript에 대한 답변이 있으므로 EQL 전문화 프로그램을 사용할 수있는 Common Lisp에서 어떻게 수행되는지 살펴 보겠습니다.

;; define a class
(defclass some-class () ())

;; declare a generic method
(defgeneric some-method (x))

;; specialize the method for SOME-CLASS
(defmethod some-method ((x some-class)) 'default-result)

;; create an instance named *MY-OBJECT* of SOME-CLASS
(defparameter *my-object* (make-instance 'some-class))

;; specialize SOME-METHOD for that specific instance
(defmethod some-method ((x (eql *my-object*))) 'specific-result)

;; Call the method on that instance
(some-method *my-object*)
=> SPECIFIC-RESULT

;; Call the method on a new instance
(some-method (make-instance 'some-class))
=> DEFAULT-RESULT

왜?

EQL 전문화 프로그램은 디스패치 대상 인수에 eql의미 가있는 유형 (예 : 숫자, 기호 등) 이있는 경우에 유용합니다 . 일반적으로 말하면 필요하지 않으며 서브 클래스 수만큼 정의해야합니다. 문제가 필요합니다. 그러나 때로는 심볼과 같은 매개 변수에 따라 전달해야합니다. case표현 함수에서 표현식은 알려진 경우로 제한되지만 메서드는 언제든지 추가하고 제거 할 수 있습니다.

또한 인스턴스 전문화는 실행중인 응용 프로그램의 특정 객체에서 발생하는 일을 일시적으로 검사하려는 경우 디버깅 목적에 유용합니다.


5

단일 객체를 사용하여 Ruby에서이 작업을 수행 할 수도 있습니다.

class A
  def do_something
    puts "Hello!"
  end
end

obj = A.new
obj.do_something

def obj.do_something
  puts "Hello world!"
end

obj.do_something

생산 :

Hello!
Hello world!

용도와 관련하여 실제로 Ruby가 클래스 및 모듈 메소드를 수행하는 방식입니다. 예를 들면 다음과 같습니다.

def SomeClass
  def self.hello
    puts "Hello!"
  end
end

실제로 객체 hello에 싱글 톤 메소드 를 정의하고 있습니다.ClassSomeClass


모듈과 확장 방법으로 답을 확장하고 싶습니까? (새로운 방법으로 객체를 확장하는 다른 방법을 보여 주어야합니까?)
knut

@ knut : extend실제로 객체의 싱글 톤 클래스를 만든 다음 모듈을 싱글 톤 클래스로 가져 오는 것이 확실합니다 .
Linuxios

5

인스턴스 별 메소드는 런타임시 자신의 클래스를 어셈블 할 수있는 것으로 생각할 수 있습니다. 이것은 많은 글루 코드를 제거 할 수 있습니다. 그 코드는 두 클래스를 서로 연결하여 서로 대화하는 것 외에 다른 목적이 없습니다. 믹스 인 은 같은 종류의 문제에 대해 다소 구조화 된 솔루션입니다.

당신은에서 조금 고생하고 BluB 효소 역설 당신이 실제 프로그램에 기능 것을 사용했습니다 때까지 언어 기능의 값을 참조하기는 어렵습니다 본질적 것을. 따라서 효과가 있다고 생각되는 기회를 찾아서 사용해보고 어떤 일이 발생하는지보십시오.

코드에서 한 가지 방법 만 다른 클래스 그룹을 찾으십시오. 다른 두 클래스를 다른 조합으로 결합하는 것이 유일한 목적인 클래스를 찾으십시오. 다른 것을하지 않고 다른 객체로 호출을 전달하는 메소드를 찾으십시오. 복잡한 작성 패턴을 사용하여 인스턴스화 된 클래스를 찾으십시오 . 그것들은 모두 인스턴스 별 방법으로 대체 될 수있는 잠재적 인 후보자입니다.


1

다른 답변은 이것이 동적 객체 지향 언어의 공통 기능인 방법과 일급 함수 객체 (예 : C #의 대리자, C ++의 연산자 ()를 재정의하는 객체)가있는 정적 언어에서 사소하게 에뮬레이트하는 방법을 보여주었습니다 . 이러한 기능이없는 정적 언어에서는 더 어렵지만 전략 패턴과 해당 구현을 전략에 단순히 위임하는 메소드의 조합을 사용하여 달성 할 수 있습니다. 이것은 실제로 C #에서 대리자를 사용하여 수행하는 것과 동일하지만 구문이 약간 더 복잡합니다.


0

C # 및 대부분의 다른 유사한 언어에서 이와 같은 작업을 수행 할 수 있습니다.

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}

1
프로그래머는 대한 개념 질문과 답변이 일을 설명 할 것으로 예상된다. 설명 대신 코드 덤프를 던지는 것은 IDE에서 화이트 보드로 코드를 복사하는 것과 같습니다. 친숙하고 때로는 이해할 수 있지만 이상하게 느껴집니다. 화이트 보드에는 컴파일러가 없습니다
gnat

0

개념적으로, Java와 같은 언어에서 클래스의 모든 인스턴스는 동일한 메소드를 가져야하지만 중첩 클래스와 결합하여 추가 간접 계층을 추가하지 않는 것처럼 보이게 할 수 있습니다.

예를 들어, 클래스 FooQuackerBase메소드를 포함 하는 정적 추상 중첩 클래스 quack(Foo)와을 파생하는 다른 정적 중첩 클래스를 QuackerBase정의 quack(Foo)할 수있는 경우 ( 각각 자체 정의가 있는 외부 클래스가 quacker유형 필드 인 QuackerBase경우) 중첩 된 클래스 중 하나의 인스턴스를 식별하도록 해당 필드를 설정하십시오. 그렇게 한 후, 호출 quacker.quack(this)하면 quack해당 필드에 인스턴스가 지정된 클래스 의 메소드 가 실행됩니다 .

이것은 일반적인 패턴이므로 Java에는 적절한 유형을 자동으로 선언하는 메커니즘이 포함되어 있습니다. 이러한 메커니즘은 실제로 가상 메소드와 선택적으로 필요로하는 정적 클래스를 사용하여 수행 할 수없는 작업을 수행하지는 않지만, 단일 메소드를 대신하여 단일 메소드를 실행하는 것이 유일한 클래스를 생성하는 데 필요한 상용구의 양을 크게 줄입니다. 다른 수업.


0

나는 이것이 루비, 그루비 및 자바 스크립트 (및 많은 다른 것들)와 같은 "동적"언어의 정의라고 믿는다. 동적은 (적어도 부분적으로) 클래스 인스턴스가 어떻게 동작하는지 동적으로 재정의하는 능력을 말합니다.

일반적으로 훌륭한 OO 관행은 아니지만 많은 동적 언어 프로그래머에게는 OO 원칙이 최우선 순위가 아닙니다.

몽키 패칭과 같은 까다로운 작업을 단순화하여 클래스 인스턴스를 조정하여 닫히지 않은 방식으로 닫힌 라이브러리와 상호 작용할 수 있습니다.


0

나는 그것이 좋은 일이라고 말하지는 않지만 이것은 파이썬에서 사소하게 가능합니다. 나는 머리 위에 좋은 유스 케이스를 가질 수 없지만 그것들이 존재한다고 확신합니다.

    class Foo(object):
        def __init__(self, thing):
            if thing == "Foo":
                def print_something():
                    print "Foo"
            else:
                def print_something():
                    print "Bar"
            self.print_something = print_something

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