Ich möchte zwischen zwei verschiedenen Arten der objektorientierten Programmierung unterscheiden
- Simulationist: Ihre Objekte stellen reale Domänenobjekte dar. Sie haben sie so programmiert, dass sie alle mit dieser Domäne verbundenen Funktionen ausführen. Auf diese Weise programmierte Objekte haben wahrscheinlich viele veränderbare Status und versteckte Kollaborateure, die zur Implementierung dieser Funktionalität verwendet werden.
- Datensätze + Funktionen: Ihre Objekte sind nur Datenbündel und Funktionen, die über diese Daten ausgeführt werden. Auf diese Weise programmierte Objekte sind eher unveränderlich, übernehmen weniger Verantwortung und ermöglichen es, Mitarbeiter einzuschleusen.
Als Faustregel gilt, dass ein auf die erste Weise programmiertes Objekt mehr Methoden und void
Methoden haben wird als auf die zweite Weise. Angenommen, wir wollten einen Flugsimulator schreiben und eine Flugzeugklasse entwerfen. Wir hätten so etwas wie:
class Plane {
void accelerate();
void deccelerate();
void toggleRightFlaps();
void toggleLeftFlaps();
void turnRudderRight();
void turnRudderLeft();
void deployLandingGear();
void liftLandingGear();
// etc.
void tick() throws PlaneCrashedException;
}
Dies ist vielleicht etwas extremer, als man es sich vorstellen könnte, aber es bringt den Punkt auf den Punkt. Wenn Sie diese Art von Schnittstelle implementieren möchten, müssen Sie sich im Objekt befinden:
- Alle Informationen über den Zustand der Flugzeugausrüstung.
- Alle Informationen über die Geschwindigkeit / Beschleunigung des Flugzeugs.
- Die Bildwiederholfrequenz unserer Simulation (um Tick zu implementieren).
- Vollständige Details zum 3D-Modell der Simulation und ihrer Physik zur Implementierung von Tick.
Das Schreiben eines Komponententests für ein im Modus geschriebenes Objekt ist absolut schwierig, weil:
- Sie müssen zu Beginn Ihres Tests all die vielen verschiedenen Daten und Mitarbeiter angeben, die dieses Objekt benötigt (das Einsetzen dieser Daten kann sehr mühsam sein).
- Wenn Sie eine Methode testen möchten, treten zwei Probleme auf: a) Die Schnittstelle stellt häufig nicht genügend Daten zum Testen bereit (sodass Sie am Ende Mocks / Reflection verwenden müssen, um die Erwartungen zu überprüfen). B) Es sind viele Komponenten gebunden in eine, die Sie bei jedem Test überprüfen müssen.
Grundsätzlich beginnen Sie mit einer Schnittstelle, die vernünftig aussieht und gut zur Domäne passt, aber die Schönheit der Simulation hat Sie dazu verleitet, ein Objekt zu erstellen, das wirklich schwer zu testen ist.
Sie können jedoch Objekte erstellen, die denselben Zweck erfüllen. Sie möchten Ihre Plane
in kleinere Teile bremsen . Haben Sie ein PlaneParticle
, das die physikalischen Teile des Flugzeugs, die Position, Geschwindigkeit, Beschleunigung, Roll, Gieren usw. usw. verfolgt und diese freilegt und manipulieren kann. Dann könnte ein PlaneParts
Objekt den Status von verfolgen. Sie würden Schiff tick()
zu einem ganz anderen Ort, sagen einen haben PlanePhysics
Objekt, zB durch parametriert, die Schwerkraft, die ein bestimmtes kennt PlaneParticle
und ein PlaneParts
neues , wie auszuspucken PlaneParticle
. All dies könnte völlig unveränderlich sein, obwohl es nicht unbedingt ein Beispiel sein muss.
Sie haben jetzt folgende Vorteile beim Testen:
- Jede einzelne Komponente hat weniger zu tun und ist einfacher einzurichten.
- Sie können Ihre Komponenten isoliert testen.
- Diese Objekte können mit dem Freilegen ihrer Einbauten davonkommen (insbesondere wenn sie unveränderlich gemacht werden), so dass man keine Klugheit braucht, um sie zu messen.
Hier ist der Trick: Der zweite objektorientierte Ansatz, den ich beschrieben habe, kommt der funktionalen Programmierung sehr nahe. Vielleicht sind in reinen Funktionsprogrammen Ihre Datensätze und Ihre Funktionen getrennt und nicht in Objekten miteinander verbunden. Auf jeden Fall würde ein Funktionsprogramm sicherstellen, dass all diese Dinge. Was ich denke, macht Unit-Tests wirklich einfach
- Kleine Einheiten (kleiner Zustandsraum).
- Funktionen mit minimaler Eingabe (keine versteckten Eingaben).
- Funktionen mit minimaler Ausgabe.
Funktionale Programmierung fördert diese Dinge (aber man kann in jedem Paradigma schlechte Programme schreiben), aber sie sind in objektorientierten Programmen erreichbar. Und ich möchte weiter betonen, dass funktionale Programmierung und objektorientierte Programmierung nicht inkompatibel sind.