Gli oggetti costruiti con la stessa classe possono avere definizioni di metodi univoche?

14

So che questa sembra una domanda strana, dal momento che il punto di due o più oggetti che condividono la stessa classe è che il loro comportamento è lo stesso, cioè i loro metodi sono identici.

Tuttavia, sono curioso di sapere se esistono linguaggi OOP che consentono di ridefinire i metodi degli oggetti nello stesso modo in cui è possibile assegnare valori diversi per i loro campi. Il risultato sarebbero oggetti costruiti dalla stessa classe che non mostrano più lo stesso identico comportamento.

Se non mi sbaglio, puoi fare questo JavaScript? Insieme a questa domanda, chiedo, perché qualcuno dovrebbe voler fare questo?

    
posta Niko Bellic 10.11.2014 - 16:24
fonte

10 risposte

9

I metodi nella maggior parte dei linguaggi OOP (class-based) sono corretti per tipo.

JavaScript è basato su prototipi, non su base di classe e quindi puoi sovrascrivere i metodi su una base per istanza perché non esiste una netta distinzione tra "classe" e oggetto; in realtà, una "classe" in JavaScript è un oggetto che è come un modello per come dovrebbero funzionare le istanze.

Qualsiasi linguaggio che consente funzioni di prima classe, Scala, Java 8, C # (tramite delegati) ecc., può comportarsi come se avessi override del metodo peristanza; dovresti definire un campo con un tipo di funzione e poi sovrascriverlo in ogni istanza.

Scala ha un'altra possibilità; In Scala, puoi creare singleton oggetto (usando la parola chiave object invece della parola chiave class), così puoi estendere la tua classe e sovrascrivere i metodi, risultando in una nuova istanza di quella classe base con overrides.

Perché qualcuno dovrebbe farlo? Potrebbero esserci dozzine di ragioni. Potrebbe essere che il comportamento abbia bisogno di essere definito più strettamente di quanto consentirebbe l'uso di varie combinazioni di campi. Potrebbe anche mantenere il codice disaccoppiato e organizzato meglio. In generale, tuttavia, ritengo questi casi più rari e spesso esiste una soluzione più semplice che utilizza valori di campo.

    
risposta data 10.11.2014 - 16:38
fonte
6

È difficile indovinare la motivazione della tua domanda, quindi alcune possibili risposte potrebbero o potrebbero non indirizzare il tuo vero interesse.

Anche in alcuni linguaggi non prototipici è possibile approssimare questo effetto.

In Java, ad esempio, una classe interna anonima è molto vicina a ciò che stai descrivendo: puoi creare e creare un'istanza di una sottoclasse dell'originale, ignorando solo il metodo oi metodi che desideri. La classe risultante sarà una instanceof della classe originale, ma non sarà la stessa classe.

Per quale motivo vorresti farlo? Con le espressioni lambda Java 8, penso che molti dei migliori casi d'uso scompaiano. Con le versioni precedenti di Java, almeno, questo può evitare una proliferazione di classi banali e di uso limitato. Cioè, quando hai un gran numero di casi d'uso correlati, che differiscono solo in un piccolo modo funzionale, puoi crearli quasi al volo (quasi), con la differenza comportamentale iniettata nel momento in cui ne hai bisogno.

Detto questo, anche pre-J8, questo può essere spesso rifattorizzato per spostare la differenza in un campo o tre e iniettarli nel costruttore. Con J8, ovviamente, il metodo stesso può essere iniettato nella classe, anche se potrebbe esserci la tentazione di farlo quando un altro refactoring può essere più pulito (se non così bello).

    
risposta data 10.11.2014 - 18:52
fonte
6

Hai chiesto qualsiasi lingua che fornisca metodi per istanza. C'è già una risposta per Javascript, quindi vediamo come è fatto in Common Lisp, dove puoi usare gli specialisti 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

Perché?

Gli specialisti EQL sono utili quando si suppone che l'argomento oggetto del dispatch abbia un tipo per il quale eql ha senso: un numero, un simbolo, ecc. In generale, non ne hai bisogno, e hai semplicemente per definire il maggior numero di sottoclassi necessarie per il tuo problema. Ma a volte, devi solo inviare un messaggio secondo un parametro che è, per esempio, un simbolo: un'espressione case sarebbe limitata ai casi noti nella funzione di dispacciamento, mentre i metodi possono essere aggiunti e rimossi in qualsiasi momento.

Inoltre, la specializzazione sulle istanze è utile ai fini del debug, quando si desidera esaminare temporaneamente ciò che accade con un oggetto specifico nella propria applicazione in esecuzione.

    
risposta data 10.11.2014 - 17:02
fonte
5

Puoi farlo anche in Ruby usando gli oggetti 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

produce:

Hello!
Hello world!

Come per gli usi, questo è in realtà il modo in cui Ruby fa i metodi di classe e modulo. Ad esempio:

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

In realtà definisce un metodo singleton hello su Class oggetto SomeClass .

    
risposta data 10.11.2014 - 18:15
fonte
5

Puoi pensare ai metodi per istanza che ti consentono di assemblare la tua classe in fase di runtime. Questo può eliminare un sacco di codice di colla, quel codice che non ha altro scopo che mettere insieme due classi per parlare tra loro. I Mixins sono una soluzione un po 'più strutturata per lo stesso tipo di problemi.

Stai soffrendo un po 'del blub paradosso , essenzialmente che è difficile vedere il valore di una funzione linguistica fino a quando non hai usato quella funzione in un programma reale. Cerca quindi opportunità in cui ritieni che potrebbe funzionare, provalo e guarda cosa succede.

Cerca nel tuo codice gruppi di classi che differiscono solo di un metodo. Cerca le classi il cui unico scopo è quello di combinare due altre classi in combinazioni diverse. Cerca metodi che non facciano altro che passare una chiamata attraverso un altro oggetto. Cerca le classi che vengono istanziate utilizzando complessi schemi di creazione . Questi sono tutti potenziali candidati che devono essere sostituiti da metodi per istanza.

    
risposta data 10.11.2014 - 18:31
fonte
1

Altre risposte hanno mostrato come questa sia una caratteristica comune dei linguaggi orientati agli oggetti dinamici e come possa essere emulata banalmente in un linguaggio statico che dispone di oggetti funzione di prima classe (ad es. delegati in c #, oggetti che sostituiscono l'operatore () in c ++). Nei linguaggi statici che mancano di tale funzione è più difficile, ma può comunque essere ottenuto utilizzando una combinazione del modello di strategia e un metodo che semplicemente ne delega l'implementazione alla strategia. Questa è in effetti la stessa cosa che si farebbe in C # con i delegati, ma la sintassi è un po 'più confusa.

    
risposta data 11.11.2014 - 12:03
fonte
0

Puoi fare qualcosa di simile in C # e nella maggior parte delle altre lingue simili.

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}
    
risposta data 10.11.2014 - 19:53
fonte
0

Concettualmente, anche se in un linguaggio come Java tutte le istanze di una classe devono avere gli stessi metodi, è possibile farlo apparire come se non lo facessero aggiungendo un ulteriore livello di riferimento indiretto, eventualmente in combinazione con classi nidificate .

Ad esempio, se una classe Foo può definire una classe nidificata astratta statica QuackerBase che contiene un metodo quack(Foo) , così come molte altre classi nidificate statiche derivanti da QuackerBase , ciascuna con la propria definizione di quack(Foo) , quindi se la classe esterna ha un campo quacker di tipo QuackerBase , allora può impostare quel campo per identificare un'istanza (possibilmente singleton) di una qualsiasi delle sue classi nidificate. Dopo averlo fatto, invocando quacker.quack(this) verrà eseguito il metodo quack della classe di cui è stata assegnata l'istanza a quel campo.

Poiché questo è uno schema abbastanza comune, Java include meccanismi per dichiarare automaticamente i tipi appropriati. Tali meccanismi non stanno facendo nulla che non possa essere fatto semplicemente usando metodi virtuali e classi statiche nidificate opzionalmente, ma riducono enormemente la quantità di standard necessario per produrre una classe il cui unico scopo è quello di eseguire un singolo metodo per conto di un'altra classe.

    
risposta data 10.11.2014 - 21:14
fonte
0

Credo che sia la definizione di un linguaggio "dinamico" come rubino, groovy e amp; Javascript (e molti altri). La dinamica si riferisce (almeno in parte) alla capacità di ridefinire dinamicamente il comportamento di un'istanza di classe al volo.

Non è una pratica OO in generale, ma per molti programmatori linguistici dinamici i principi OO non sono la loro priorità assoluta.

Semplifica alcune operazioni complicate come Monkey-Patching in cui è possibile modificare un'istanza di classe per consentire all'utente di interagire con una libreria chiusa in un modo che non avevano previsto.

    
risposta data 10.11.2014 - 23:54
fonte
0

Non sto dicendo che sia una buona cosa da fare, ma questo è banalmente possibile in Python. Non posso farmi una buona idea, ma sono sicuro che esistano.

    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()
    
risposta data 11.11.2014 - 22:48
fonte

Leggi altre domande sui tag