JavaRush /Java-Blog /Random-DE /Entwerfen von Klassen und Schnittstellen (Übersetzung des...
fatesha
Level 22

Entwerfen von Klassen und Schnittstellen (Übersetzung des Artikels)

Veröffentlicht in der Gruppe Random-DE
Entwerfen von Klassen und Schnittstellen (Übersetzung des Artikels) - 1

Inhalt

  1. Einführung
  2. Schnittstellen
  3. Schnittstellenmarkierungen
  4. Funktionale Schnittstellen, statische Methoden und Standardmethoden
  5. Abstrakte Klassen
  6. Unveränderliche (permanente) Klassen
  7. Anonymer Unterricht
  8. Sichtweite
  9. Nachlass
  10. Mehrfachvererbung
  11. Vererbung und Zusammensetzung
  12. Verkapselung
  13. Abschlussklassen und Methoden
  14. Was weiter
  15. Quellcode herunterladen

1. EINLEITUNG

Unabhängig davon, welche Programmiersprache Sie verwenden (und Java ist keine Ausnahme), ist die Befolgung guter Designprinzipien der Schlüssel zum Schreiben von sauberem, verständlichem und überprüfbarem Code. und es auch so gestalten, dass es langlebig ist und die Problemlösung leicht unterstützt. In diesem Teil des Tutorials werden wir die grundlegenden Bausteine ​​der Java-Sprache besprechen und einige Designprinzipien vorstellen, um Ihnen dabei zu helfen, bessere Designentscheidungen zu treffen. Genauer gesagt werden wir Schnittstellen und Schnittstellen mit Standardmethoden (eine neue Funktion in Java 8), abstrakte und endgültige Klassen, unveränderliche Klassen, Vererbung und Zusammensetzung besprechen und die Sichtbarkeits- (oder Zugänglichkeits-)Regeln, die wir kurz angesprochen haben, noch einmal besprechen Teil 1 Lektion „Wie man Objekte erstellt und zerstört“ .

2. SCHNITTSTELLEN

In der objektorientierten Programmierung bildet das Konzept der Schnittstellen die Grundlage für die Entwicklung von Verträgen . Kurz gesagt definieren Schnittstellen eine Reihe von Methoden (Verträgen) und jede Klasse, die Unterstützung für diese bestimmte Schnittstelle benötigt, muss eine Implementierung dieser Methoden bereitstellen: eine ziemlich einfache, aber leistungsstarke Idee. Viele Programmiersprachen verfügen über Schnittstellen in der einen oder anderen Form, aber insbesondere Java bietet dafür Sprachunterstützung. Werfen wir einen Blick auf eine einfache Schnittstellendefinition in Java.
package com.javacodegeeks.advanced.design;

public interface SimpleInterface {
void performAction();
}
Im obigen Snippet SimpleInterfacedeklariert die von uns aufgerufene Schnittstelle nur eine Methode namens performAction. Der Hauptunterschied zwischen Schnittstellen und Klassen besteht darin, dass Schnittstellen beschreiben, wie der Kontakt aussehen soll (sie deklarieren eine Methode), aber nicht deren Implementierung bereitstellen. Allerdings können Schnittstellen in Java komplexer sein: Sie können verschachtelte Schnittstellen, Klassen, Zählungen, Anmerkungen und Konstanten umfassen. Zum Beispiel:
package com.javacodegeeks.advanced.design;

public interface InterfaceWithDefinitions {
    String CONSTANT = "CONSTANT";

    enum InnerEnum {
        E1, E2;
    }

    class InnerClass {
    }

    interface InnerInterface {
        void performInnerAction();
    }

    void performAction();
}
In diesem komplexeren Beispiel gibt es mehrere Einschränkungen, die Schnittstellen bedingungslos für verschachtelte Konstrukte und Methodendeklarationen auferlegen, und diese werden vom Java-Compiler durchgesetzt. Erstens ist jede Methodendeklaration in einer Schnittstelle öffentlich (und kann nur öffentlich sein), auch wenn sie nicht explizit deklariert wird. Somit sind die folgenden Methodendeklarationen äquivalent:
public void performAction();
void performAction();
Es ist erwähnenswert, dass jede einzelne Methode in einer Schnittstelle implizit als abstract deklariert wird und sogar diese Methodendeklarationen äquivalent sind:
public abstract void performAction();
public void performAction();
void performAction();
Deklarierte Konstantenfelder sind nicht nur öffentlich , sondern auch implizit statisch und mit final gekennzeichnet . Daher sind auch die folgenden Deklarationen gleichwertig:
String CONSTANT = "CONSTANT";
public static final String CONSTANT = "CONSTANT";
Schließlich werden verschachtelte Klassen, Schnittstellen oder Zählungen nicht nur öffentlich , sondern auch implizit als statisch deklariert . Diese Deklarationen sind beispielsweise auch äquivalent zu:
class InnerClass {
}

static class InnerClass {
}
Welchen Stil Sie wählen, hängt von Ihren persönlichen Vorlieben ab, aber wenn Sie diese einfachen Eigenschaften von Schnittstellen kennen, können Sie sich unnötiges Tippen ersparen.

3. Schnittstellenmarkierung

Eine Markierungsschnittstelle ist eine spezielle Art von Schnittstelle, die keine Methoden oder andere verschachtelte Konstrukte aufweist. Wie die Java-Bibliothek es definiert:
public interface Cloneable {
}
Schnittstellenmarkierungen sind per se keine Verträge, aber eine einigermaßen nützliche Technik zum „Anhängen“ oder „Verknüpfen“ einer bestimmten Eigenschaft mit einer Klasse. In Bezug auf Cloneable wird die Klasse beispielsweise als klonbar markiert, aber die Art und Weise, wie dies implementiert werden kann oder sollte, ist nicht Teil der Schnittstelle. Ein weiteres sehr bekanntes und weit verbreitetes Beispiel für einen Schnittstellenmarker ist Serializable:
public interface Serializable {
}
Diese Schnittstelle markiert die Klasse als für die Serialisierung und Deserialisierung geeignet und gibt wiederum nicht an, wie dies implementiert werden kann oder sollte. Schnittstellenmarkierungen haben ihren Platz in der objektorientierten Programmierung, erfüllen jedoch nicht den Hauptzweck einer Schnittstelle, ein Vertrag zu sein. 

4. FUNKTIONALE SCHNITTSTELLEN, STANDARDMETHODEN UND STATISCHE METHODEN

Seit der Veröffentlichung von Java 8 haben Schnittstellen einige sehr interessante neue Funktionen erhalten: statische Methoden, Standardmethoden und automatische Konvertierung von Lambdas (funktionale Schnittstellen). Im Abschnitt „Schnittstellen“ haben wir die Tatsache hervorgehoben, dass Schnittstellen in Java nur Methoden deklarieren, aber nicht deren Implementierung bereitstellen können. Bei einer Standardmethode ist das anders: Eine Schnittstelle kann eine Methode mit dem Standardschlüsselwort markieren und eine Implementierung dafür bereitstellen. Zum Beispiel:
package com.javacodegeeks.advanced.design;

public interface InterfaceWithDefaultMethods {
    void performAction();

    default void performDefaulAction() {
        // Implementation here
    }
}
Auf Instanzebene könnten die Standardmethoden von jeder Schnittstellenimplementierung überschrieben werden, aber Schnittstellen können jetzt auch statische Methoden enthalten, zum Beispiel: package com.javacodegeeks.advanced.design;
public interface InterfaceWithDefaultMethods {
    static void createAction() {
        // Implementation here
    }
}
Man könnte sagen, dass die Bereitstellung der Implementierung in der Schnittstelle den gesamten Zweck der Vertragsprogrammierung zunichte macht. Aber es gibt viele Gründe, warum diese Funktionen in die Java-Sprache eingeführt wurden, und egal wie nützlich oder verwirrend sie sind, sie stehen Ihnen und Ihrer Nutzung zur Verfügung. Eine andere Sache sind funktionale Schnittstellen, die sich als sehr nützliche Ergänzungen der Sprache erwiesen haben. Im Grunde ist eine funktionale Schnittstelle eine Schnittstelle, auf der nur eine abstrakte Methode deklariert ist. RunnableDie Standardbibliotheksschnittstelle ist ein sehr gutes Beispiel für dieses Konzept.
@FunctionalInterface
public interface Runnable {
    void run();
}
Der Java-Compiler behandelt funktionale Schnittstellen unterschiedlich und kann eine Lambda-Funktion in eine funktionale Schnittstellenimplementierung umwandeln, wenn dies sinnvoll ist. Betrachten wir die folgende Funktionsbeschreibung: 
public void runMe( final Runnable r ) {
    r.run();
}
Um diese Funktion in Java 7 und niedriger aufzurufen, muss eine Implementierung der Schnittstelle bereitgestellt werden Runnable(z. B. mithilfe anonymer Klassen). In Java 8 reicht es jedoch aus, eine Implementierung der run()-Methode mithilfe der Lambda-Syntax bereitzustellen:
runMe( () -> System.out.println( "Run!" ) );
Darüber hinaus weist die Annotation @FunctionalInterface (Annotationen werden in Teil 5 des Tutorials ausführlich behandelt) darauf hin, dass der Compiler prüfen kann, ob eine Schnittstelle nur eine abstrakte Methode enthält, sodass künftige Änderungen an der Schnittstelle diese Annahme nicht verletzen .

5. ABSTRAKTE KLASSEN

Ein weiteres interessantes Konzept, das von der Java-Sprache unterstützt wird, ist das Konzept abstrakter Klassen. Abstrakte Klassen ähneln in gewisser Weise den Schnittstellen in Java 7 und sind der Standardmethodenschnittstelle in Java 8 sehr ähnlich. Im Gegensatz zu regulären Klassen kann eine abstrakte Klasse nicht instanziiert, aber in Unterklassen unterteilt werden (weitere Einzelheiten finden Sie im Abschnitt „Vererbung“). Noch wichtiger ist, dass abstrakte Klassen abstrakte Methoden enthalten können: eine spezielle Art von Methode ohne Implementierung, genau wie eine Schnittstelle. Zum Beispiel:
package com.javacodegeeks.advanced.design;

public abstract class SimpleAbstractClass {
    public void performAction() {
        // Implementation here
    }

    public abstract void performAnotherAction();
}
In diesem Beispiel ist die Klasse SimpleAbstractClassals abstrakt deklariert und enthält eine deklarierte abstrakte Methode. Abstrakte Klassen sind sehr nützlich; die meisten oder sogar einige Teile der Implementierungsdetails können von vielen Unterklassen gemeinsam genutzt werden. Wie dem auch sei, sie lassen die Tür immer noch offen und ermöglichen es Ihnen, das Verhalten jeder Unterklasse mithilfe abstrakter Methoden anzupassen. Es ist erwähnenswert, dass abstrakte Klassen im Gegensatz zu Schnittstellen, die nur öffentliche Deklarationen enthalten können, die volle Leistungsfähigkeit der Barrierefreiheitsregeln nutzen können, um die Sichtbarkeit einer abstrakten Methode zu steuern.

6. SOFORTKURSE

Unveränderlichkeit wird heutzutage in der Softwareentwicklung immer wichtiger. Der Aufstieg von Multi-Core-Systemen hat viele Probleme im Zusammenhang mit der gemeinsamen Nutzung von Daten und der Parallelität aufgeworfen. Es ist jedoch definitiv ein Problem aufgetreten: Ein geringer (oder gar kein) veränderlicher Zustand führt zu einer besseren Erweiterbarkeit (Skalierbarkeit) und einer einfacheren Argumentation über das System. Leider bietet die Java-Sprache keine angemessene Unterstützung für die Unveränderlichkeit von Klassen. Durch die Kombination verschiedener Techniken ist es jedoch möglich, unveränderliche Klassen zu entwerfen. Zunächst müssen alle Felder der Klasse final sein (als final markiert ). Das ist ein guter Anfang, aber keine Garantie. 
package com.javacodegeeks.advanced.design;

import java.util.Collection;

public class ImmutableClass {
    private final long id;
    private final String[] arrayOfStrings;
    private final Collection<String> collectionOfString;
}
Stellen Sie zweitens sicher, dass die Initialisierung korrekt ist: Wenn ein Feld eine Referenz auf eine Sammlung oder ein Array ist, weisen Sie diese Felder nicht direkt aus Konstruktorargumenten zu, sondern erstellen Sie stattdessen Kopien. Dadurch wird sichergestellt, dass der Status der Sammlung oder des Arrays außerhalb der Sammlung oder des Arrays nicht geändert wird.
public ImmutableClass( final long id, final String[] arrayOfStrings,
        final Collection<String> collectionOfString) {
    this.id = id;
    this.arrayOfStrings = Arrays.copyOf( arrayOfStrings, arrayOfStrings.length );
    this.collectionOfString = new ArrayList<>( collectionOfString );
}
Und schließlich die Sicherstellung des ordnungsgemäßen Zugriffs (Getter). Für Sammlungen muss die Unveränderlichkeit als Wrapper bereitgestellt werden  Collections.unmodifiableXxx: Bei Arrays besteht die einzige Möglichkeit, echte Unveränderlichkeit bereitzustellen, darin, eine Kopie bereitzustellen, anstatt einen Verweis auf das Array zurückzugeben. Dies ist aus praktischer Sicht möglicherweise nicht akzeptabel, da es stark von der Größe des Arrays abhängt und den Garbage Collector enorm belasten kann.
public String[] getArrayOfStrings() {
    return Arrays.copyOf( arrayOfStrings, arrayOfStrings.length );
}
Selbst dieses kleine Beispiel vermittelt eine gute Vorstellung davon, dass Unveränderlichkeit in Java noch kein erstklassiger Bürger ist. Die Dinge können kompliziert werden, wenn eine unveränderliche Klasse ein Feld hat, das auf ein Objekt einer anderen Klasse verweist. Diese Klassen sollten ebenfalls unveränderlich sein, es gibt jedoch keine Möglichkeit, dies sicherzustellen. Es gibt mehrere gute Java-Quellcode-Analysatoren wie FindBugs und PMD, die sehr hilfreich sein können, indem sie Ihren Code überprüfen und auf häufige Java-Programmierfehler hinweisen. Diese Tools sind große Freunde eines jeden Java-Entwicklers.

7. ANONYME KLASSEN

In der Zeit vor Java 8 waren anonyme Klassen die einzige Möglichkeit, sicherzustellen, dass Klassen im laufenden Betrieb definiert und sofort instanziiert wurden. Der Zweck anonymer Klassen bestand darin, den Standardwert zu reduzieren und eine kurze und einfache Möglichkeit zur Darstellung von Klassen als Datensatz bereitzustellen. Werfen wir einen Blick auf die typische altmodische Methode, einen neuen Thread in Java zu erstellen:
package com.javacodegeeks.advanced.design;

public class AnonymousClass {
    public static void main( String[] args ) {
        new Thread(
            // Example of creating anonymous class which implements
            // Runnable interface
            new Runnable() {
                @Override
                public void run() {
                    // Implementation here
                }
            }
        ).start();
    }
}
In diesem Beispiel Runnablewird die Implementierung von interface sofort als anonyme Klasse bereitgestellt. Obwohl mit anonymen Klassen einige Einschränkungen verbunden sind, besteht der Hauptnachteil ihrer Verwendung in der sehr ausführlichen Konstruktsyntax, zu der Java als Sprache verpflichtet ist. Selbst eine anonyme Klasse, die nichts tut, erfordert jedes Mal, wenn sie geschrieben wird, mindestens fünf Codezeilen.
new Runnable() {
   @Override
   public void run() {
   }
}
Glücklicherweise werden mit Java 8, Lambda und funktionalen Schnittstellen all diese Stereotypen bald verschwinden, und das Schreiben von Java-Code wird endlich wirklich prägnant aussehen.
package com.javacodegeeks.advanced.design;

public class AnonymousClass {
    public static void main( String[] args ) {
        new Thread( () -> { /* Implementation here */ } ).start();
    }
}

8. SICHTBARKEIT

Wir haben bereits in Teil 1 des Tutorials ein wenig über Sichtbarkeits- und Barrierefreiheitsregeln in Java gesprochen. In diesem Teil werden wir dieses Thema noch einmal aufgreifen, allerdings im Kontext der Unterklassenbildung. Entwerfen von Klassen und Schnittstellen (Übersetzung des Artikels) - 2Die Sichtbarkeit auf verschiedenen Ebenen ermöglicht oder verhindert, dass Klassen andere Klassen oder Schnittstellen sehen (z. B. wenn sie sich in verschiedenen Paketen befinden oder ineinander verschachtelt sind) oder Unterklassen die Methoden, Konstruktoren und Felder ihrer Eltern sehen und darauf zugreifen. Im nächsten Abschnitt, Vererbung, werden wir dies in Aktion sehen.

9. Vererbung

Vererbung ist eines der Schlüsselkonzepte der objektorientierten Programmierung und dient als Grundlage für den Aufbau einer Klasse von Beziehungen. In Kombination mit Sichtbarkeits- und Zugänglichkeitsregeln ermöglicht die Vererbung, dass Klassen in einer Hierarchie gestaltet werden, die erweitert und verwaltet werden kann. Auf konzeptioneller Ebene wird die Vererbung in Java mithilfe von Unterklassen und dem Schlüsselwort „extends“ zusammen mit der übergeordneten Klasse implementiert. Eine Unterklasse erbt alle öffentlichen und geschützten Elemente der übergeordneten Klasse. Darüber hinaus erbt eine Unterklasse die paketprivaten Elemente ihrer übergeordneten Klasse, wenn sich beide (Unterklasse und Klasse) im selben Paket befinden. Unabhängig davon, was Sie entwerfen möchten, ist es sehr wichtig, sich an den Mindestsatz an Methoden zu halten, die eine Klasse öffentlich oder ihren Unterklassen zur Verfügung stellt. Schauen wir uns beispielsweise die Klasse Parentund ihre Unterklasse an Child, um den Unterschied in den Sichtbarkeitsstufen und deren Auswirkungen zu veranschaulichen.
package com.javacodegeeks.advanced.design;

public class Parent {
    // Everyone can see it
    public static final String CONSTANT = "Constant";

    // No one can access it
    private String privateField;
    // Only subclasses can access it
    protected String protectedField;

    // No one can see it
    private class PrivateClass {
    }

    // Only visible to subclasses
    protected interface ProtectedInterface {
    }

    // Everyone can call it
    public void publicAction() {
    }

    // Only subclass can call it
    protected void protectedAction() {
    }

    // No one can call it
    private void privateAction() {
    }

    // Only subclasses in the same package can call it
    void packageAction() {
    }
}
package com.javacodegeeks.advanced.design;

// Resides in the same package as parent class
public class Child extends Parent implements Parent.ProtectedInterface {
    @Override
    protected void protectedAction() {
        // Calls parent's method implementation
        super.protectedAction();
    }

    @Override
    void packageAction() {
        // Do nothing, no call to parent's method implementation
    }

    public void childAction() {
        this.protectedField = "value";
    }
}
Vererbung ist an sich schon ein sehr umfangreiches Thema mit vielen feinen Java-spezifischen Details. Es gibt jedoch ein paar Regeln, die leicht zu befolgen sind und viel dazu beitragen können, die Klassenhierarchie kurz zu halten. In Java kann jede Unterklasse alle geerbten Methoden ihrer übergeordneten Klasse überschreiben, es sei denn, sie wurde als endgültig deklariert. Es gibt jedoch keine spezielle Syntax oder kein spezielles Schlüsselwort, um eine Methode als überschrieben zu markieren, was zu Verwirrung führen kann. Aus diesem Grund wurde die Annotation @Override eingeführt : Wenn Ihr Ziel darin besteht, eine geerbte Methode zu überschreiben, verwenden Sie bitte die Annotation @Override , um dies prägnant anzugeben. Ein weiteres Dilemma, mit dem Java-Entwickler beim Design ständig konfrontiert sind, ist der Aufbau von Klassenhierarchien (mit konkreten oder abstrakten Klassen) im Vergleich zur Implementierung von Schnittstellen. Wir empfehlen dringend, nach Möglichkeit Schnittstellen gegenüber Klassen oder abstrakten Klassen zu bevorzugen. Schnittstellen sind leichter, einfacher zu testen und zu warten und minimieren außerdem die Nebenwirkungen von Implementierungsänderungen. Viele fortgeschrittene Programmiertechniken, wie zum Beispiel das Erstellen von Proxy-Klassen in der Java-Standardbibliothek, basieren stark auf Schnittstellen.

10. Mehrfachvererbung

Im Gegensatz zu C++ und einigen anderen Sprachen unterstützt Java keine Mehrfachvererbung: In Java kann jede Klasse nur eine direkte übergeordnete Klasse haben (wobei die Klasse Objectan der Spitze der Hierarchie steht). Allerdings kann eine Klasse mehrere Schnittstellen implementieren, und daher ist das Stapeln von Schnittstellen die einzige Möglichkeit, Mehrfachvererbung in Java zu erreichen (oder zu simulieren).
package com.javacodegeeks.advanced.design;

public class MultipleInterfaces implements Runnable, AutoCloseable {
    @Override
    public void run() {
        // Some implementation here
    }

    @Override
    public void close() throws Exception {
       // Some implementation here
    }
}
Die Implementierung mehrerer Schnittstellen ist tatsächlich ziemlich leistungsfähig, aber oft führt die Notwendigkeit, die Implementierung immer wieder zu verwenden, zu einer tiefen Klassenhierarchie, um die mangelnde Unterstützung von Java für Mehrfachvererbung zu überwinden. 
public class A implements Runnable {
    @Override
    public void run() {
        // Some implementation here
    }
}
// Class B wants to inherit the implementation of run() method from class A.
public class B extends A implements AutoCloseable {
    @Override
    public void close() throws Exception {
       // Some implementation here
    }
}
// Class C wants to inherit the implementation of run() method from class A
// and the implementation of close() method from class B.
public class C extends B implements Readable {
    @Override
    public int read(java.nio.CharBuffer cb) throws IOException {
       // Some implementation here
    }
}
Und so weiter ... Die aktuelle Version von Java 8 behebt das Problem mit der standardmäßigen Methodeninjektion. Aufgrund der Standardmethoden stellen Schnittstellen tatsächlich nicht nur einen Vertrag, sondern auch eine Implementierung bereit. Daher erben Klassen, die diese Schnittstellen implementieren, auch automatisch diese implementierten Methoden. Zum Beispiel:
package com.javacodegeeks.advanced.design;

public interface DefaultMethods extends Runnable, AutoCloseable {
    @Override
    default void run() {
        // Some implementation here
    }

    @Override
    default void close() throws Exception {
       // Some implementation here
    }
}

// Class C inherits the implementation of run() and close() methods from the
// DefaultMethods interface.
public class C implements DefaultMethods, Readable {
    @Override
    public int read(java.nio.CharBuffer cb) throws IOException {
       // Some implementation here
    }
}
Bedenken Sie, dass die Mehrfachvererbung ein sehr mächtiges, aber gleichzeitig gefährliches Werkzeug ist. Das bekannte Diamond-of-Death-Problem wird oft als großer Fehler bei der Implementierung der Mehrfachvererbung angeführt, der Entwickler dazu zwingt, Klassenhierarchien sehr sorgfältig zu entwerfen. Leider sind auch Java 8-Schnittstellen mit Standardmethoden von diesen Mängeln betroffen.
interface A {
    default void performAction() {
    }
}

interface B extends A {
    @Override
    default void performAction() {
    }
}

interface C extends A {
    @Override
    default void performAction() {
    }
}
Beispielsweise kann das folgende Codefragment nicht kompiliert werden:
// E is not compilable unless it overrides performAction() as well
interface E extends B, C {
}
An dieser Stelle kann man mit Recht sagen, dass Java als Sprache immer versucht hat, die Eckfälle der objektorientierten Programmierung zu vermeiden, aber mit der Weiterentwicklung der Sprache tauchen plötzlich einige dieser Fälle auf. 

11. Vererbung und Zusammensetzung

Glücklicherweise ist Vererbung nicht die einzige Möglichkeit, Ihre Klasse zu entwerfen. Eine weitere Alternative, die viele Entwickler für viel besser halten als die Vererbung, ist die Komposition. Die Idee ist sehr einfach: Anstatt eine Hierarchie von Klassen zu erstellen, müssen diese aus anderen Klassen zusammengesetzt werden. Schauen wir uns dieses Beispiel an:
// E is not compilable unless it overrides performAction() as well
interface E extends B, C {
}
Die Klasse Vehiclebesteht aus einem Motor und Rädern (sowie vielen anderen Teilen, die der Einfachheit halber weggelassen wurden). Man kann jedoch sagen, dass eine Klasse Vehicleauch eine Engine ist und daher durch Vererbung entworfen werden kann. 
public class Vehicle extends Engine {
    private Wheels[] wheels;
    // ...
}
Welche Designlösung wäre richtig? Die allgemeinen Kernrichtlinien sind als IS-A-Prinzipien (ist) und HAS-A-Prinzipien (enthält) bekannt. IS-A ist eine Vererbungsbeziehung: Eine Unterklasse erfüllt auch die Klassenspezifikation der übergeordneten Klasse und eine Variation der übergeordneten Klasse. Unterklasse erweitert ihre übergeordnete Klasse. Wenn Sie wissen möchten, ob eine Entität eine andere erweitert, führen Sie einen Übereinstimmungstest durch – IS -A (ist).") Daher ist HAS-A eine Kompositionsbeziehung: Eine Klasse besitzt (oder enthält) ein Objekt, das in den meisten Fällen aus mehreren Gründen besser funktioniert als IS-A: 
  • Design ist flexibler;
  • Das Modell ist stabiler, da sich Änderungen nicht über die Klassenhierarchie verbreiten;
  • Eine Klasse und ihre Zusammensetzung sind im Vergleich zur Zusammensetzung, die eine übergeordnete Klasse und ihre Unterklasse eng miteinander verbindet, lose gekoppelt.
  • Der logische Gedankengang in einer Klasse ist einfacher, da alle seine Abhängigkeiten darin an einer Stelle enthalten sind. 
Unabhängig davon hat die Vererbung ihre Berechtigung und löst eine Reihe bestehender Designprobleme auf vielfältige Weise, sodass sie nicht vernachlässigt werden sollte. Bitte berücksichtigen Sie diese beiden Alternativen beim Entwurf Ihres objektorientierten Modells.

12. VERKAPSELUNG.

Das Konzept der Kapselung in der objektorientierten Programmierung besteht darin, alle Implementierungsdetails (wie Betriebsmodus, interne Methoden usw.) vor der Außenwelt zu verbergen. Die Vorteile der Kapselung liegen in der Wartbarkeit und einfachen Änderung. Die interne Implementierung der Klasse ist verborgen, die Arbeit mit Klassendaten erfolgt ausschließlich über öffentliche Methoden der Klasse (ein echtes Problem, wenn Sie eine Bibliothek oder ein Framework entwickeln, das von vielen Leuten verwendet wird). Die Kapselung in Java wird durch Sichtbarkeits- und Zugänglichkeitsregeln erreicht. In Java gilt es als Best Practice, Felder niemals direkt, sondern nur über Getter und Setter verfügbar zu machen (es sei denn, die Felder sind als final markiert). Zum Beispiel:
package com.javacodegeeks.advanced.design;

public class Encapsulation {
    private final String email;
    private String address;

    public Encapsulation( final String email ) {
        this.email = email;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }
}
Dieses Beispiel erinnert an das, was in der Java-Sprache JavaBeans genannt wird: Standard-Java-Klassen werden nach einer Reihe von Konventionen geschrieben, von denen eine den Zugriff auf Felder nur mit Getter- und Setter-Methoden zulässt. Wie wir bereits im Abschnitt „Vererbung“ betont haben, halten Sie sich bitte immer an den Mindestwerbevertrag in einer Klasse und nutzen Sie dabei die Prinzipien der Kapselung. Alles, was nicht öffentlich sein sollte, sollte privat werden (oder geschützt/paketprivat, je nachdem, welches Problem Sie lösen). Dies wird sich auf lange Sicht auszahlen, da Sie Gestaltungsfreiheit haben, ohne dass grundlegende Änderungen erforderlich sind (oder diese zumindest minimiert werden). 

13. ABSCHLUSSKLASSEN UND METHODEN

In Java gibt es eine Möglichkeit zu verhindern, dass eine Klasse eine Unterklasse einer anderen Klasse wird: Die andere Klasse muss als endgültig deklariert werden. 
package com.javacodegeeks.advanced.design;

public final class FinalClass {
}
Das gleiche letzte Schlüsselwort in einer Methodendeklaration verhindert, dass Unterklassen die Methode überschreiben. 
package com.javacodegeeks.advanced.design;

public class FinalMethod {
    public final void performAction() {
    }
}
Es gibt keine allgemeinen Regeln für die Entscheidung, ob eine Klasse oder Methoden endgültig sein sollen oder nicht. Endgültige Klassen und Methoden schränken die Erweiterbarkeit ein und es ist sehr schwierig, vorauszudenken, ob eine Klasse vererbt werden soll oder nicht, oder ob eine Methode in Zukunft überschrieben werden soll oder nicht. Dies ist besonders wichtig für Bibliotheksentwickler, da Designentscheidungen wie diese die Anwendbarkeit der Bibliothek erheblich einschränken könnten. Die Java-Standardbibliothek verfügt über mehrere Beispiele für finale Klassen, die bekannteste ist die String-Klasse. Diese Entscheidung wurde frühzeitig getroffen, um jegliche Versuche von Entwicklern zu verhindern, eine eigene, „bessere“ Lösung für die Implementierung von String zu finden. 

14. WAS KOMMT NÄCHSTES?

In diesem Teil der Lektion haben wir die Konzepte der objektorientierten Programmierung in Java behandelt. Wir haben auch einen kurzen Blick auf die Vertragsprogrammierung geworfen, einige funktionale Konzepte angesprochen und gesehen, wie sich die Sprache im Laufe der Zeit entwickelt hat. Im nächsten Teil der Lektion werden wir Generika kennenlernen und erfahren, wie sie unsere Herangehensweise an die Typensicherheit in der Programmierung verändern. 

15. QUELLCODE HERUNTERLADEN

Sie können die Quelle hier herunterladen - advanced-java-part-3 Quelle: So entwerfen Sie Klassen und
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION