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

14

Eu sei que isso parece uma pergunta estranha, já que o ponto de dois ou mais objetos compartilhando 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 forma que você pode atribuir diferentes valores para seus campos. O resultado seriam objetos construídos da mesma classe que não exibem exatamente o mesmo comportamento.

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

    
por Niko Bellic 10.11.2014 / 16:24
fonte

10 respostas

9

Os métodos na maioria das linguagens OOP (baseadas em classes) são fixados por tipo.

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

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

Scala tem outra posibilidade; No Scala, você pode criar singletons de objeto (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 isso? Pode haver dezenas de razões. Pode ser que o comportamento precise de mim mais rigidamente definido do que o uso de várias combinações de campo permitiria. Também poderia manter o código desacoplado e organizado melhor. Em geral, no entanto, acho que esses casos são mais raros e geralmente há uma solução mais direta usando valores de campo.

    
por 10.11.2014 / 16:38
fonte
6

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

Mesmo em alguns idiomas que não são protótipos, é 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 deseja. A classe resultante será uma instanceof da 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 de uso restrito. Ou seja, quando você tem um grande número de casos de uso relacionados, diferindo apenas de uma maneira minúscula e funcional, você pode criá-los quase na hora (quase), com a diferença de comportamento injetada no momento em que você precisar. / p>

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

    
por 10.11.2014 / 18:52
fonte
6

Você solicitou qualquer idioma que forneça métodos por instância. Já existe uma resposta para o Javascript, então vamos ver como é 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ê?

EQL-specializers é útil quando o argumento sujeito a dispatching deve ter um tipo para o qual eql faz sentido: um número, um símbolo, etc. Geralmente, você não precisa dele, e você simplesmente tem para definir quantas subclasses forem necessárias para o seu problema. Mas às vezes, você só precisa despachar de acordo com um parâmetro que é, por exemplo, um símbolo: uma expressão case seria limitada aos casos conhecidos na função de dispatch, 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.

    
por 10.11.2014 / 17:02
fonte
5

Você também pode fazer isso em 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 a usos, é como o Ruby faz 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 hello no Class object SomeClass .

    
por 10.11.2014 / 18:15
fonte
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 um monte de código de cola, aquele código que não tem outro propósito senão colocar duas classes juntas para falar umas com as outras. Os Mixins são uma solução um pouco mais estruturada para o mesmo tipo de problemas.

Você está sofrendo um pouco com o paradoxo do blub , essencialmente, é difícil ver o valor de um recurso de idioma até que você tenha usado esse recurso em um programa real. Então, procure oportunidades em que você acha que pode funcionar, experimente e veja o que acontece.

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

    
por 10.11.2014 / 18:31
fonte
1

Outras respostas mostraram como essa é uma característica comum de linguagens dinâmicas orientadas a objetos e como ela pode ser emulada trivialmente em uma linguagem estática que possui objetos de função de primeira classe (por exemplo, delegados em c #, objetos que substituem operator () em c ++). Em linguagens estáticas que não têm essa função, é mais difícil, mas ainda pode ser obtida usando uma combinação do padrão Strategy e um método que simplesmente delega sua implementação à estratégia. Esta é a mesma coisa que você faria em c # com delegados, mas a sintaxe é um pouco mais confusa.

    
por 11.11.2014 / 12:03
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
    }
}
    
por 10.11.2014 / 19:53
fonte
0

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

Por exemplo, se uma classe Foo puder definir uma classe aninhada abstrata estática QuackerBase , que contém um método quack(Foo) , bem como várias outras classes aninhadas estáticas derivadas de QuackerBase , cada uma com sua própria definição de quack(Foo) , em seguida, se a classe externa tiver um campo quacker do tipo QuackerBase , ela poderá definir esse campo para identificar uma instância (possivelmente singleton) de qualquer uma de suas classes aninhadas. Depois de fazer isso, invocar quacker.quack(this) executará o método quack 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 nada que não possa ser feito simplesmente usando métodos virtuais e classes estáticas opcionalmente aninhadas, mas reduzem bastante a quantidade de clichê necessária para produzir uma classe cujo único propósito seja executar um único método em nome de outra aula.

    
por 10.11.2014 / 21:14
fonte
0

Acredito que essa é a definição de uma linguagem "dinâmica" como ruby, groovy & 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.

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

Simplifica algumas operações complicadas, como o Monkey-Patch, 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 previram.

    
por 10.11.2014 / 23:54
fonte
0

Eu não estou dizendo que é uma boa coisa a fazer, mas isso é trivialmente possível em Python. Eu não posso pensar em um bom caso de uso fora do topo da minha cabeça, mas tenho certeza 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()
    
por 11.11.2014 / 22:48
fonte