Methode einer Basisklasse "ausgrauen"

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Poseidonius
User
Beiträge: 63
Registriert: Montag 23. Januar 2006, 08:58

Hallo Ihr Pythonprofis,

ich lasse eine Klasse A von einer Klasse B erben. Dabei soll die Nutzung einer bestimmten Methode von B aus einer Instanz von A heraus zu einer Fehlermeldung führen. Also überschreibe ich die Methode in A und werfe darin eine exception? Oder gibt es da was rafinierteres?

Grüße und ein schönes Wochenende für Euch

Poseidonius
deets

Das funktioniert, aber nur, solange der Benutzer der Klassen sich brav benimmt. Aber wer will, ruft halt

B.foo(self)

auf, statt

mein_a.foo()

womit dann deine Plaene ueber den Haufen sind.

Warum willst du das ueberhaupt machen? Ist die Methode ein Implementierungsdetail? Dann markier sie als private, mit einem, oder sogar vielleicht hier mal ausnahmsweise zwei Unterstrichen. Dann "darf" keiner sie benutzen.

Letztlich verhindern kann auch das es nicht, und darum wuerde ich es auch erst gar nicht versuchen.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Poseidonius: Nein, das ist schon richtig so. Allerdings verstößt dieses Design gegen gegen das Liskov-Substitutions-Prinzip, da dessen zentrale Forderung, dass Unterklassen jederzeit für die Oberklasse substituiert werden können, bei dir nicht erfüllt ist. Wenn man zum Contract der Methode 'kann eine DarfNichtAufUnterklassenAufgerufenWerdenException werfen' hinzufügt, dann ist zwar das LSP formal wieder erfüllt, allerdings zu dem Preis, dass um jede Stelle im Client-Code, wo die Methode aufgerufen wird, ein try-except-Block gewickelt werden muss. Dann kann man auch gleich wieder von Hand dispatchen:

Code: Alles auswählen

if type(x) is A:
    x.method_which_can_only_be_invoked_on_base_class_instance()
Explizites Type Dispatching, egal ob mittels if-Block oder try-except-Block, ist ziemlich un-OO. Ich würde ein anderes Design wählen. Beschreib doch mal, warum du das tun möchtest, vielleicht fällt jemandem ja eine andere Lösung ein.
In specifications, Murphy's Law supersedes Ohm's.
Poseidonius
User
Beiträge: 63
Registriert: Montag 23. Januar 2006, 08:58

Hallo deets, hallo pillmuncher,

danke für Eure raschen Anworten ! Offenbar ist mein Konzept falsch ...

Hinter der Basisklasse B versteckt sich eine Kommunikationsschnittstelle, die mit B.read() und B.write() zwei Funktionen implementiert. Von dieser Basisklasse sollen A und Z erben, wobei erstere eine Inputklasse und letztgenannte eine Ausgabeklasse darstellen sollen. Dabei wollte ich vermeiden, dass jemand mit einer Inputklasse alles durcheinander bringt und Daten versenden will, also ein A.write() aufruft. Die Klasse B soll im System die spezielle Funktionalität der Schnittstelle kapseln und austauschbar sein.


Grüße

Poseidonius
deets

dann waere die Loesung wohl eher zwei weitere Klassen anzubieten, welche einmal input und einmal output bereitstellen. So wie es zB in der Java-IO InputStream und OutputStream gibt. Oder das ganze durch Komposition zu erledigen.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Poseidonius: Gemäß dem Interface Segregation Principle sollte jeder Programmteil nur mit denjenigen Interfaces konfrontiert sein, die dort auch verwendet werden. Wenn eine aus einer Funktion heraus nur Daten gesendet werden sollen, dann sollte sie auch nur einen Writer (nennen wir ihn mal so) erwarten, selbst wenn sie tatsächlich einen Stream mir read() und write() Methoden übergeben bekommt, zB so:

Code: Alles auswählen

class Reader(object):
    def read(self):
        pass

class Writer(object):
    def write(self, data):
        pass

def combine(reader, writer):
    class Stream(reader.__class__, writer.__class__):
        read = reader.read
        write = writer.write
    return Stream()

#-----------------------------------------------------------------

def foo(reader):
    print reader.read()

def bar(writer):
    writer.write('hey joe!')

def reply_double(stream):
    x = stream.read()
    stream.write(x*2)

#-----------------------------------------------------------------

class MyReader(Reader):
    def read(self):
        ''' spezielle MyReader-Implementierung '''
        return 'hello'

class YourWriter(Writer):
    def write(self, data):
        ''' spezielle YourWriter-Implementierung '''
        print data


#-----------------------------------------------------------------

stream = combine(MyReader(), YourWriter())

foo(stream)
bar(stream)
reply_double(stream)
Entscheidend ist nicht, von welcher Klasse das übergebene Objekt tatsächlich ist, sondern dass dessen Klasse ein Subtyp der erwarteten ist. Stream-Klassen können wie oben auch dynamisch erzeugt werden und sind dann sowohl Reader als auch Writer.
In specifications, Murphy's Law supersedes Ohm's.
Antworten