2009-05-09 14 views
16

Nachdem ich viele Blogs, Foreneinträge und mehrere Apple Dokumente gelesen habe, Ich weiß immer noch nicht, ob umfangreiche Unterklassen in Objective-C eine weise Sache zu tun ist oder nicht.Ist Unterklasse in Objective-C eine schlechte Praxis?

Nehmen Sie zum Beispiel die folgenden Fall:

Say Ich entwickle ein Puzzle-Spiel, das viele Elemente hat. Alle diese Elemente teilen eine bestimmte Menge des gleichen Verhaltens. Dann in meiner Sammlung von Elementen, verschiedene Gruppen von Elementen teilen gleich Verhalten, Scheidungs ​​Gruppen von Gruppen, etc ...

Also, nach der Bestimmung, was erbt von dem, was, entschied ich mich Unterklasse aus der Vergessenheit. Und warum sollte ich nicht? Angesichts der Leichtigkeit Tweaking allgemeine Verhalten nimmt mit diesem Modell, ich denke, ich habe etwas erreicht OOP ist gemeint für.

Aber - und das ist die Quelle meiner Frage - Apple-Erwähnungen Delegierten verwenden, Datenquelle Methoden und informelle Protokolle für Subklassifizieren. Es macht mir wirklich den Verstand, warum?

Es scheint zwei Lager zu geben. Diejenigen, die für Unterklassen sind, diejenigen, die nicht dafür sind. Es hängt offenbar vom persönlichen Geschmack ab. Ich frage mich, was die Vor- und Nachteile der massiven Unterklassenbildung sind und keine massive Unterklassenbildung?

Um es einzupacken, ist meine Frage einfach: Bin ich richtig? Und warum oder warum nicht?

+10

Kriem, Apple spricht über die Verwendung von Kakao im Allgemeinen, nichts mit Objective-C zu tun. Für jedes einzelne Projekt müssen Sie entscheiden, wie Sie Ihre Anwendung und Codebasis am besten einrichten. In Apples Fall haben sie Cocoa (insbesondere AppKit/UIKit) mit MVC- und IoC-Paradigmen eingerichtet, also schlagen sie vor, dass Sie keine Dinge wie NSControl usw. selbst ableiten, wenn Sie stattdessen Delegaten verwenden könnten. Zusammenfassend: Diese Warnung gilt speziell für den Cocoa-Rahmen, nicht für Objective-C im Allgemeinen. –

+0

@Jason: Das ist eine ziemlich gute Antwort, warum es als ein einfacher Kommentar verlassen? – mouviciel

+0

@mouviciel - Ich denke einfach nicht, denke ich. Jetzt gibt es eine Reihe von Antworten, die im Grunde dasselbe sagen, also lohnt es sich nicht, mehr Lärm hinzuzufügen :) –

Antwort

10

Persönlich folge ich dieser Regel: Ich kann eine Unterklasse erstellen, wenn sie die Liskov substitution principle respektiert.

+0

Gute Information. Ich denke, das trifft auf meinen Fall zu. Aber - und das ist die Quelle meiner Frage - erwähnt Apple die Verwendung von Delegaten, Datenquellenmethoden und informellen Protokollen zugunsten von Unterklassen. Es macht mir wirklich den Verstand, warum? - Ich werde die Frage bearbeiten, um dies zu betonen. – Kriem

+1

Ich vermute, dass es in den meisten Fällen viel einfacher ist, Delegierte zu verwenden, daher die Empfehlung von Apple. Subclassing mag angebracht sein, aber es wäre schwieriger, es richtig zu machen. Nur meine Meinung. –

+1

Ich denke, dass diese Definition sehr allgemein ist. Wenn man die Mathematik durchliest, sagt man im Grunde, dass alles, was die Superklasse benutzt, in der Lage sein sollte, die Unterklasse ohne Veränderung zu benutzen.Anstatt zu verstehen, wann eine Unterklasse zu haben ist, heißt es einfach, dass Sie Unterklassen davon abhalten sollten, eine Klasse zu brechen. Diese Definition eliminiert auch ganz allgemeine Kategorien von Unterklassen - wie abstrakte Oberklassen, die niemals direkt verwendet werden sollen, nur untergeordnete ... –

4

Im Allgemeinen sollten Sie keine Unterklasse von API-Klassen verwenden, wenn Delegaten usw. vorhanden sind, die das ausführen, was Sie tun möchten. In Ihrem eigenen Code ist Unterklassierung oft schöner, aber es hängt wirklich von Ihren Zielen ab, z. Wenn Sie eine API bereitstellen, sollten Sie eine delegate-basierte API bereitstellen, statt eine Unterklasse zu übernehmen.

Beim Umgang mit APIs hat das Subklassieren mehr potenzielle Fehler - z. Wenn eine Klasse in der Klassenhierarchie eine neue Methode erhält, die den gleichen Namen wie Ihr Zusatz hat, machen Sie Bruchkram. Und wenn Sie eine nützliche/helferartige Funktion bereitstellen, besteht die Möglichkeit, dass in Zukunft etwas Ähnliches der eigentlichen Klasse hinzugefügt wird, die Sie unterklassifiziert haben, und das könnte effizienter sein, aber Ihre Überschreibung wird es verbergen.

3

Ich denke wirklich, es hängt davon ab, was Sie versuchen zu tun. Wenn das Puzzle-Spiel, das du im Beispiel beschreibst, wirklich eine Reihe einzigartiger Elemente hat, die gemeinsame Attribute haben, und es keine Klassen gibt - sagen wir zum Beispiel "NSPuzzlePiece" -, die deinen Bedürfnissen entsprechen, sehe ich kein Problem mit umfangreichen Unterklassen.

Nach meiner Erfahrung sind Delegaten, Datenquellmethoden und informelle Protokolle viel nützlicher, wenn Apple eine Klasse bereitgestellt hat, die bereits etwas tut, was dem entspricht, was Sie tun möchten.

Angenommen, Sie erstellen eine App, die eine Tabelle verwendet.Es gibt (und ich spreche hier vom iPhone SDK, da ich dort Erfahrung habe) eine Klasse UITableView, die alle kleinen Feinheiten der Erstellung einer Tabelle für die Interaktion mit dem Benutzer macht, und es ist viel effizienter, eine Datenquelle für eine zu definieren Instanz von UITableView, als es ist, UITableView vollständig abzuleiten und seine Methoden neu zu definieren oder zu erweitern, um sein Verhalten anzupassen.

Ähnliche Konzepte gelten für Delegaten und Protokolle. Wenn Sie Ihre Ideen in Apples Klassen einfügen können, ist es normalerweise einfacher (und wird reibungsloser funktionieren), dies zu tun und Datenquellen, Delegaten und Protokolle zu verwenden, als Ihre eigenen Unterklassen zu erstellen. Es hilft Ihnen, zusätzliche Arbeit zu vermeiden und Zeit zu verschwenden, und ist in der Regel weniger fehleranfällig. Apples Klassen haben sich darum gekümmert, Funktionen effizient zu machen und zu debuggen. Je mehr Sie mit ihnen arbeiten können, desto weniger Fehler wird Ihr Programm auf lange Sicht haben.

6

Es gibt nichts Falsches bei der Verwendung der Vererbung in Objective-C. Apple benutzt es ziemlich oft. Zum Beispiel ist in Cocoa-touch der Vererbungsbaum von UIButton UIControl: UIView: UIResponder: NSObject.

Ich denke Martin hat einen wichtigen Punkt bei der Erwähnung des Liskov-Substitutionsprinzips gefunden. Außerdem erfordert die korrekte Verwendung der Vererbung, dass der Implementierer der Unterklasse über ein tiefgehendes Wissen über die Superklasse verfügt. Wenn Sie jemals Schwierigkeiten hatten, eine nicht-triviale Klasse in einem komplexen Framework zu erweitern, wissen Sie, dass es immer eine Lernkurve gibt. Außerdem "dringen" Implementierungsdetails der Superklasse häufig in die Unterklasse ein, was ein großer Schmerz in der @ $ & für Framework-Builder ist.

Apple entschied sich in vielen Fällen für Delegierung, um diese Probleme zu beheben. Nicht-triviale Klassen wie UIApplication stellen allgemeine Erweiterungspunkte über ein Delegatobjekt zur Verfügung, sodass die meisten Entwickler sowohl eine einfachere Lernkurve als auch eine lose gekoppelte Methode zum Hinzufügen anwendungsspezifischen Verhaltens haben. Eine direkte Erweiterung der UIApplication ist selten erforderlich.

In Ihrem Fall, für Ihren anwendungsspezifischen Code, verwenden Sie die Techniken, mit denen Sie vertraut sind und die am besten für Ihr Design geeignet sind. Vererbung ist ein großartiges Werkzeug, wenn es entsprechend verwendet wird.

Ich sehe häufig Anwendungsprogrammierer Lehren aus Framework-Designs und versuchen, sie auf ihre Anwendung Code anwenden (das ist in Java, C++ und Python-Welten sowie Objective-C üblich). Während es gut ist, über die Auswahl der Framework-Designer nachzudenken und zu verstehen, gelten diese Lektionen nicht immer für den Anwendungscode.

7

Subklassifizieren hat seine Vorteile, aber es hat auch einige Nachteile. Als allgemeine Regel versuche ich, Implementierungsvererbung zu vermeiden und stattdessen Schnittstellenvererbung und Delegierung zu verwenden.

Einer der Gründe, warum ich das tue, ist, dass wenn Sie die Implementierung erben, Sie mit Problemen enden können, wenn Sie Methoden überschreiben, aber ihren (manchmal undokumentierten) Vertrag nicht einhalten. Darüber hinaus finde ich gehende Klassenhierarchien mit Implementierungsvererbung schwierig, da Methoden auf jeder Ebene überschrieben oder implementiert werden können. Schließlich können Sie bei der Unterklassenbildung nur eine Schnittstelle erweitern, die Sie nicht eingrenzen können. Dies führt zu undichten Abstraktionen. Ein gutes Beispiel dafür ist java.util.Stack, das java.util.Vector erweitert. Ich sollte einen Stapel nicht als Vektor behandeln können. Nur so kann der Benutzer die Schnittstelle umgehen.

Andere haben das Liskow-Substitutionsprinzip erwähnt. Ich denke, dass die Verwendung dieses Problems das Problem java.util.Stack ausgeräumt hätte, aber es kann auch zu sehr tiefen Klassenhierarchien führen, um sicherzustellen, dass Klassen nur die Methoden bekommen, die sie haben sollen.

Stattdessen gibt es bei der Schnittstellenvererbung im Wesentlichen keine Klassenhierarchie, da sich die Schnittstellen selten gegenseitig erweitern müssen.Die Klassen implementieren einfach die Schnittstellen, die sie benötigen, und können daher vom Verbraucher richtig behandelt werden. Da keine Implementation-Vererbung vorhanden ist, können die Benutzer dieser Klassen ihr Verhalten aufgrund früherer Erfahrungen mit einer Elternklasse nicht ableiten.

Am Ende ist es eigentlich egal, auf welchem ​​Weg Sie gehen. Beide sind absolut akzeptabel. Es geht eher darum, mit was Sie sich wohler fühlen und was die Frameworks, mit denen Sie arbeiten, fördern. Wie das alte Sprichwort sagt: "Wenn in Rom wie Römer tun."

12

Delegierung ist ein Mittel zur Verwendung der Kompositionstechnik, um einige Aspekte der Codierung zu ersetzen, für die Sie sonst Unterklassen verwenden würden. Als solches läuft es auf die uralte Frage der Aufgabe hinaus, die eine große Sache benötigt, die viel zu tun weiß, oder wenn Sie ein loses Netzwerk von spezialisierten Objekten haben (ein sehr untypisches Modell der Verantwortung).

Die Verwendung einer Kombination von Delegaten und Protokollen (um zu definieren, was die Delegierten tun sollen) bietet ein hohes Maß an Flexibilität des Verhaltens und eine einfache Programmierung - zurück zu diesem Liskov-Substitutionsprinzip, wenn Sie sich von einer Unterklasse ablösen Seien Sie vorsichtig, Sie tun nichts, was ein Benutzer der ganzen Klasse für unerwartet halten würde. Aber wenn Sie einfach ein Delegiertenobjekt erstellen, dann haben Sie viel weniger dafür verantwortlich, nur dass die Delegiertenmethoden, die Sie implementieren, tun, was das eine Protokoll verlangt, darüber hinaus ist es Ihnen egal.

Es gibt immer noch viele gute Gründe für die Verwendung von Unterklassen. Wenn Sie wirklich Verhalten und Variablen zwischen mehreren Klassen geteilt haben, kann das für die Unterklasse sehr sinnvoll sein. Aber wenn Sie das Delegate-Konzept nutzen können, werden Sie Ihre Klassen oft einfacher erweitern oder verwenden, wie Sie es vom Designer nicht erwartet haben.

Ich bin eher ein Fan von formalen Protokollen als informellen, weil nicht nur formale Protokolle sicherstellen, dass Sie die Methoden eine Klasse behandeln Sie als Delegat erwarten, sondern auch, weil die Protokolldefinition ist ein natürlicher Ort um zu dokumentieren, was Sie von einem Delegaten erwarten, der diese Methoden implementiert.

2

Mein Eindruck von ADC's Schwerpunkt 'gegen' Unterklassen hat mehr mit dem Vermächtnis zu tun, wie sich das Betriebssystem entwickelt hat ... damals (Mac Classic aka os9), als C++ die primäre Schnittstelle zu den meisten war mac toolbox, subclassing war der De-facto-Standard, damit ein Programmierer das Verhalten gängiger OS-Funktionen modifizieren konnte (und das war in der Tat manchmal ein Dorn im Auge und bedeutete, dass man sehr vorsichtig sein musste, dass alle Modifikationen vorhersagbar waren und hat kein Standardverhalten gebrochen).

dieser gesagt, mein Eindruck von Betonung des ADC gegen Subklassifizieren ist nicht einen Fall hervor setzen ohne Vererbung Klassenhierarchie einer Anwendung für die Gestaltung, sondern darauf hin, dass in der neue Art und Weise, Dinge zu tun (dh OSX) gibt es in den meisten Fällen geeignetere Mittel, um Standardverhalten anzupassen, ohne eine Unterklasse zu benötigen.

Entwerfen Sie auf diese Weise die Architektur Ihres Puzzles so robust wie möglich und nutzen Sie die Vererbung so, wie Sie es für richtig halten!

freuen uns auf Ihre coole neue Puzzle-Anwendung zu sehen!

| K <

+0

Dies ist eine interessante Idee, aber in der Tat wurden die wichtigsten Design-Prinzipien von Cocoa lange vor C++ eine gemeinsame Programmiersprache für Mac OS etabliert. Cocoa legt seinen Schwerpunkt auf Delegierung und Komposition von NEXTSTEP, das so entworfen wurde, weil die Verantwortlichen der Ansicht waren, dass die Komposition eine zuverlässigere Methode sei, aus wiederverwendbaren Komponenten große Softwaresysteme zu erstellen. Und diese Idee geht wirklich den ganzen Weg zurück zu Smalltalk, und das Model-View-Conroller Design-Muster. –

+0

Es gibt auch ein Element der schmerzhaften Lektionen zerbrechlicher Basisklassen, die im Taligent-Projekt gelernt wurden. Das war ein gemeinsames Apple-IBM-Projekt, das zum Teil versuchte, einen Gesamtrahmen für die Entwicklung von Apps zu schaffen. Ihre Metapher "Menschen, Orte und Dinge" ist es wert, nur für einen Einblick in das Framework-Design studiert zu werden. Dieses Projekt war wahrscheinlich die größte Demonstration, wie C++ als Sprache für grundlegendes Framework-Design versagt (ich sage das als professioneller C++ - Entwickler). –

+0

Kleine Korrektur, C++ war nie die primäre Schnittstelle zu den meisten MacOS Toolbox. Es war immer C und Pascal. –

4

Bitte lesen Sie die Apple-Dokumentation "Adding behavior to a Cocoa program"!. Unter "Vererbung von einem Kakao-Klasse" Abschnitt, siehe den zweiten Absatz.Apple erwähnt klar, dass Subclassing der primäre Weg ist, anwendungsspezifisches Verhalten dem Framework hinzuzufügen (bitte beachten Sie, FRAMEWORK).
Das MVC-Muster verbietet die Verwendung von Unterklassen oder Subtypen nicht vollständig. Wenigstens habe ich diese Empfehlung weder von Apple noch von anderen gesehen (wenn ich verpasst habe, bitte zögern Sie nicht, mich auf die richtige Informationsquelle zu verweisen). Wenn Sie nur innerhalb Ihrer Anwendung api-Klassen ableiten, gehen Sie bitte voran, niemand hält Sie davon ab, aber achten Sie darauf, dass das Verhalten der Klasse/API nicht als Ganzes gebrochen wird. Subclassing ist eine großartige Möglichkeit, die Funktionalität der Framework API zu erweitern. Wir sehen auch viele Unterklassen innerhalb der Apple IOS Framework-APIs. Als Entwickler muss man darauf achten, dass die Implementierung gut dokumentiert ist und nicht versehentlich von einem anderen Entwickler dupliziert wird. Es ist ein weiteres Ballspiel, wenn Ihre Anwendung eine Reihe von API-Klassen ist, die Sie als wiederverwendbare Komponente verteilen möchten.

IMHO, anstatt zu fragen, was die beste Praxis ist, lesen Sie zuerst die zugehörige Dokumentation gründlich, implementieren und testen Sie es. Mache dein eigenes Urteil. Du weißt am besten, was du vorhast. Es ist einfach für andere (wie ich und so viele andere) einfach Sachen aus verschiedenen Quellen im Netz zu lesen und Begriffe herumzuwerfen. Sei dein eigener Richter, es hat für mich bisher funktioniert.

0

Apple scheint tatsächlich Subclassing mit Objective-C zu entmutigen.

Es ist ein Axiom der OOP-Design zu Gunsten Zusammensetzung über die Implementierung.

Verwandte Themen