Os objetos construídos a partir da mesma classe podem ter definições de método exclusivas?

14

Eu sei que isso parece uma pergunta estranha, já que o objetivo de dois ou mais objetos compartilharem a mesma classe é que o comportamento deles é o mesmo, ou seja, seus métodos são idênticos.

No entanto, estou curioso para saber se existem linguagens OOP que permitem redefinir os métodos de objetos da mesma maneira que você pode atribuir valores diferentes para seus campos. O resultado seria objetos construídos a partir da mesma classe que não exibem mais exatamente o mesmo comportamento.

Se não me engano, você pode fazer esse JavaScript? Junto com essa pergunta, pergunto: por que alguém iria querer fazer isso?

Niko Bellic
fonte
9
O termo técnico é "baseado em protótipo". Procurar por isso fornecerá muito material sobre esse sabor do POO. (Esteja ciente de que muitas pessoas não consideram tais estruturas "classes" adequados, precisamente porque eles não têm atributos e métodos uniformes.)
Kilian Foth
1
você quer dizer como duas instâncias diferentes de um botão podem fazer coisas diferentes quando são clicadas, porque cada uma delas é conectada a um manipulador de botão diferente? Claro, sempre se pode argumentar que eles fazem a mesma coisa - eles chamam de manipulador de botões. De qualquer forma, se o seu idioma suportar delegados ou ponteiros de função, isso é fácil - você tem alguma variável de propriedade / campo / membro que retém o ponteiro de delegado ou função, e a implementação do método chama isso e você deve ir embora.
Kate Gregory
2
É complicado :) Nas linguagens em que as referências à função slinging são comuns, é fácil passar algum código para um setClickHandler()método e fazer com que instâncias diferentes da mesma classe façam coisas muito diferentes. Em idiomas que não possuem expressões lambda convenientes, é mais fácil criar uma nova subclasse anônima apenas para um novo manipulador. Tradicionalmente, os métodos de substituição eram considerados a marca registrada de uma nova classe, enquanto a definição dos valores dos atributos não era, mas especialmente com as funções do manipulador, os dois têm efeitos muito semelhantes, então a distinção se torna uma guerra por palavras.
Kilian Foth
1
Não é exatamente o que você está perguntando, mas isso soa como o padrão de design da estratégia. Aqui você tem uma instância de uma classe que efetivamente altera seu tipo em tempo de execução. É um pouco fora do assunto porque não é a linguagem que permite isso, mas vale a pena mencionar porque você disse "por que alguém iria querer fazer isso"
tony
OOP baseado em Protótipo do @Killian Forth não tem classes por definição e, portanto, não corresponde exatamente ao que o OP está perguntando. A principal diferença é que uma classe não é uma instância.
Equivale

Respostas:

9

Os métodos na maioria dos idiomas OOP (baseados em classe) são corrigidos por tipo.

O JavaScript é baseado em protótipo, não em classe e, portanto, você pode substituir os métodos em uma base por instâncias, porque não há distinção rígida entre "classe" e objeto; na realidade, uma "classe" em JavaScript é um objeto que é como um modelo de como as instâncias devem funcionar.

Qualquer linguagem que permita funções de primeira classe, Scala, Java 8, C # (via delegados) etc, pode agir como se você tivesse substituições de método por instância; você precisaria definir um campo com um tipo de função e substituí-lo em cada instância.

Scala tem outra possibilidade; No Scala, você pode criar singletons de objetos (usando a palavra-chave object em vez da palavra-chave class), para poder estender sua classe e substituir os métodos, resultando em uma nova instância dessa classe base com substituições.

Por que alguém faria isto? Pode haver dezenas de razões. Pode ser que o comportamento precise ser mais definido do que o permitido por várias combinações de campos. Também poderia manter o código dissociado e organizado melhor. No entanto, em geral, acho esses casos mais raros e geralmente há uma solução mais direta usando valores de campo.

eques
fonte
Sua primeira frase não faz sentido. O que o OOP baseado em classe tem a ver com tipos fixos?
Bergi
Quero dizer que, por tipo, o conjunto e as definições dos métodos são fixos ou, em outras palavras, para adicionar ou alterar um método, você deve criar um novo tipo (por exemplo, subtipagem).
Equivale
@ Bergi: Na OOP baseada em classe, a palavra "classe" e "tipo" significam essencialmente a mesma coisa. Como não faz sentido permitir que o tipo inteiro tenha o valor "hello", também não faz sentido que o tipo Employee tenha valores ou métodos que não pertençam à classe Employee.
slebetman
@lebetman: eu quis dizer que uma linguagem OOP baseada em classe não tem necessariamente digitação forte e aplicada estaticamente.
Bergi 11/11
@ Bergi: Esse é o significado de "mais" na resposta acima (a maioria significa não todos). Estou apenas comentando o seu comentário "o que isso tem a ver".
slebetman
6

É difícil adivinhar a motivação da sua pergunta e, portanto, algumas respostas possíveis podem ou não atender ao seu interesse real.

Mesmo em algumas linguagens sem protótipo, é possível aproximar esse efeito.

Em Java, por exemplo, uma classe interna anônima é muito próxima do que você está descrevendo - você pode criar e instanciar uma subclasse do original, substituindo apenas o método ou métodos que você deseja. A classe resultante será instanceofa classe original, mas não será a mesma classe.

Por que você quer fazer isso? Com as expressões lambda do Java 8, acho que muitos dos melhores casos de uso desaparecem. Com versões anteriores do Java, pelo menos, isso pode evitar a proliferação de classes triviais e de uso restrito. Ou seja, quando você tem um grande número de casos de uso relacionados, diferindo apenas de uma maneira funcional minúscula, é possível criá-los quase em movimento (quase), com a diferença de comportamento injetada no momento em que você precisa.

Dito isto, mesmo antes do J8, isso geralmente pode ser refatorado para mudar a diferença para um campo ou três e injetá-las no construtor. Com o J8, é claro, o próprio método pode ser injetado na classe, embora possa haver uma tentação de fazê-lo quando outra refatoração for mais limpa (se não for tão legal).

Schnitz
fonte
6

Você solicitou qualquer idioma que forneça métodos por instância. Já existe uma resposta para Javascript, então vamos ver como isso é feito no Common Lisp, onde você pode usar os especialistas em EQL:

;; 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

Por quê?

Os especialistas em EQL são úteis quando o argumento sujeito a despacho deve ter um tipo para o qual eqlfaz sentido: um número, um símbolo etc. Em geral, você não precisa dele e simplesmente precisa definir quantas subclasses necessário pelo seu problema. Mas, às vezes, você só precisa despachar de acordo com um parâmetro que é, por exemplo, um símbolo: uma caseexpressão seria limitada aos casos conhecidos na função de despacho, enquanto os métodos podem ser adicionados e removidos a qualquer momento.

Além disso, a especialização em instâncias é útil para fins de depuração, quando você deseja inspecionar temporariamente o que acontece com um objeto específico em seu aplicativo em execução.

coredump
fonte
5

Você também pode fazer isso no Ruby usando objetos singleton:

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

Produz:

Hello!
Hello world!

Quanto aos usos, é assim que Ruby efetua os métodos de classe e módulo. Por exemplo:

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

Na verdade, está definindo um método singleton hellono Classobjeto SomeClass.

Linuxios
fonte
Deseja estender sua resposta com módulos e método de extensão? (Apenas para mostrar outras maneiras de estender objetos com novos métodos)?
Knut
@knut: Tenho certeza de que, extendna verdade, apenas cria a classe singleton para o objeto e depois importa o módulo para a classe singleton.
Linuxios
5

Você pode pensar em métodos por instância como permitindo montar sua própria classe em tempo de execução. Isso pode eliminar muito código de cola, código que não tem outro objetivo senão reunir duas classes para conversar entre si. Os mixins são uma solução um pouco mais estruturada para os mesmos tipos de problemas.

Você está sofrendo um pouco com o paradoxo do blub , basicamente é difícil ver o valor de um recurso de linguagem até que você o use em um programa real. Portanto, procure oportunidades nas quais você acha que pode funcionar, tente e veja o que acontece.

Procure no seu código grupos de classes que diferem apenas por um método. Procure por classes cujo único objetivo é combinar duas outras classes em combinações diferentes. Procure métodos que não fazem nada além de passar uma chamada para outro objeto. Procure por classes instanciadas usando padrões criacionais complexos . Todos esses são candidatos em potencial a serem substituídos por métodos por instância.

Karl Bielefeldt
fonte
1

Outras respostas mostraram como esse é um recurso comum das linguagens dinâmicas orientadas a objetos e como ele pode ser emulado trivialmente em uma linguagem estática que possui objetos de função de primeira classe (por exemplo, delegados em c #, objetos que substituem operador () em c ++) . Em linguagens estáticas que carecem dessa função, é mais difícil, mas ainda pode ser alcançado usando uma combinação do padrão Strategy e um método que simplesmente delega sua implementação à estratégia. Isso é efetivamente o mesmo que você faria em c # com delegados, mas a sintaxe é um pouco mais confusa.

Jules
fonte
0

Você pode fazer algo assim em C # e na maioria dos outros idiomas semelhantes.

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}
Esben Skov Pedersen
fonte
1
Programadores trata de perguntas conceituais e espera-se que as respostas expliquem as coisas. Jogar dumps de código em vez de explicação é como copiar código do IDE para o quadro branco: pode parecer familiar e até às vezes compreensível, mas parece estranho ... apenas estranho. O quadro branco não tem compilador
gnat
0

Conceitualmente, mesmo que em uma linguagem como Java todas as instâncias de uma classe devam ter os mesmos métodos, é possível fazer parecer que não, adicionando uma camada extra de indireção, possivelmente em combinação com classes aninhadas.

Por exemplo, se uma classe Foopode definir uma classe aninhada abstrata estática QuackerBaseque contém um método quack(Foo), bem como várias outras classes aninhadas estáticas derivadas QuackerBase, cada uma com sua própria definição de quack(Foo), então se a classe externa tiver um campo quackerdo tipo QuackerBase, poderá defina esse campo para identificar uma instância (possivelmente singleton) de qualquer uma de suas classes aninhadas. Depois disso, a chamada quacker.quack(this)executará o quackmétodo da classe cuja instância foi atribuída a esse campo.

Como esse é um padrão bastante comum, o Java inclui mecanismos para declarar automaticamente os tipos apropriados. Esses mecanismos não estão realmente fazendo algo que não poderia ser feito simplesmente usando métodos virtuais e classes estáticas aninhadas opcionalmente, mas reduzem bastante a quantidade de clichê necessária para produzir uma classe cujo único objetivo é executar um único método em nome de outra classe.

supercat
fonte
0

Eu acredito que é a definição de uma linguagem "dinâmica" como ruby, groovy e Javascript (e muitos outros). Dinâmico refere-se (pelo menos em parte) à capacidade de redefinir dinamicamente como uma instância de classe pode se comportar em tempo real.

Geralmente, não é uma boa prática OO, mas para muitos programadores de linguagem dinâmica os princípios OO não são sua principal prioridade.

Isso simplifica algumas operações complicadas, como o Monkey-Patching, onde você pode ajustar uma instância de classe para permitir que você interaja com uma biblioteca fechada de uma maneira que eles não previam.

Bill K
fonte
0

Não estou dizendo que é uma coisa boa a se fazer, mas isso é trivialmente possível em Python. Não consigo pensar em um bom caso de uso, mas tenho certeza de que eles existem.

    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()
Singletoned
fonte