Programování

Navrhování oborů a metod

Splátka tohoto měsíce ve výši Techniky návrhu je druhým v minisérii sloupců o navrhování objektů. Ve sloupci z minulého měsíce, který pokrýval návrh objektů pro správnou inicializaci, jsem mluvil o tom, jak navrhovat konstruktory a inicializátory. Tento měsíc a příští měsíc budu diskutovat principy návrhu pro skutečná pole a metody třídy. Poté budu psát o finalizátorech a ukážu, jak navrhovat objekty pro správné vyčištění na konci jejich životů.

Materiál pro tento článek (vyhýbání se zvláštním hodnotám dat, použití konstant, minimalizace vazby) a další článek (maximalizace soudržnosti) může být znám mnoha čtenářům, protože tento materiál je založen na obecných principech návrhu, které jsou zcela nezávislé na programovacím jazyce Java . Nicméně, protože jsem se v průběhu let setkal s tolika kódy, které tyto principy nevyužívají, myslím si, že si zaslouží být čas od času přepracovány. Kromě toho se v tomto článku pokouším ukázat, jak tyto obecné zásady platí zejména pro jazyk Java.

Navrhování polí

Při navrhování polí je hlavním pravidlem vyhnout se použití jedné proměnné k reprezentaci více atributů třídy. Toto pravidlo můžete porušit označením zvláštních hodnot v proměnné, z nichž každá má svůj vlastní speciální význam.

Jak se zde používá, atribut je rozlišovací charakteristika objektu nebo třídy. Dva atributy a CoffeeCup objekt může být například:

  • Množství kávy, které obsahuje šálek
  • Zda je šálek čistý nebo špinavý

Chcete-li se na toto pravidlo podívat blíže, představte si, že navrhujete a CoffeeCup třída pro virtuální kavárnu popsanou v minulém měsíci Techniky návrhu sloupec. Předpokládejme, že chcete modelovat, zda byl šálek kávy ve vaší virtuální kavárně umyt a je připraven k použití dalším zákazníkem. Díky těmto informacím budete mít jistotu, že nebudete znovu používat šálek kávy, dokud nebude umytý.

Pokud se rozhodnete, že vám záleží pouze na tom, zda byl šálek vypraný, pokud je prázdný, můžete použít speciální hodnotu vnitřní káva pole, které se obvykle používá ke sledování množství kávy v šálku, představuje neprané šálek. Pokud je 473 mililitrů (16 uncí) maximální množství kávy ve vašem největším šálku, pak maximální hodnota vnitřní káva normálně by to bylo 473. Můžete tedy použít vnitřní káva hodnota, řekněme, 500 (speciální hodnota) k označení prázdného šálku, který je nepraný:

// Ve zdrojovém paketu v souborových polích / ex1 / CoffeeCup.java třída CoffeeCup {private int innerCoffee; public boolean isReadyForNextUse () {// Pokud se šálek kávy neumyje, pak // není připraven k dalšímu použití if (innerCoffee == 500) {return false; } návrat true; } public void setCustomerDone () {innerCoffee = 500; // ...} public void wash () {innerCoffee = 0; // ...} // ...} 

Tento kód dá CoffeeCup namítá požadované chování. Potíž s tímto přístupem spočívá v tom, že speciální hodnoty nejsou snadno pochopitelné a jejich kód je obtížnější změnit. I když v komentáři popisujete speciální hodnoty, může ostatním programátorům trvat déle, než pochopí, co váš kód dělá. Navíc nemusí nikdy rozumět vašemu kódu. Mohou vaši třídu používat nesprávně nebo ji změnit tak, že zavedou chybu.

Pokud například někdo později přidá do nabídky virtuální kavárny šálek o objemu 20 uncí, bylo by možné v šálku pojmout až 592 mililitrů (ml) kávy. Pokud programátor přidá novou velikost šálku, aniž by si uvědomil, že používáte 500 ml, což znamená, že šálek je třeba umýt, je pravděpodobné, že dojde k chybě. Pokud by si zákazník ve vaší virtuální kavárně koupil šálek o objemu 20 uncí, pak si vzal velký doušek o objemu 92 ml, zůstalo by mu v šálku přesně 500 ml. Zákazník by byl šokován a nespokojen, když po vypití pouze 92 ml šálek zmizel z jeho ruky a objevil se v umyvadle připravený k umytí. A i kdyby si programátor, který provedl změnu, uvědomil, že používáte speciální hodnotu, musela by být vybrána další speciální hodnota pro neumytý atribut.

Lepší přístup k této situaci je mít samostatné pole pro modelování samostatného atributu:

// Ve zdrojovém paketu v polích souboru / ex2 / CoffeeCup.java třída CoffeeCup {private int innerCoffee; soukromé booleovské potřebyPraní; public boolean isReadyForNextUse () {// Pokud se šálek kávy neumyje, pak // není připraven na další použití return! needsWashing; } public void setCustomerDone () {needsWashing = true; // ...} public void wash () {needsWashing = false; // ...} // ...} 

Tady vnitřní káva pole se používá pouze k modelování množství kávy v atributu cup. Atribut cup-needs-washing je modelován pomocí potřeby Umývání pole. Toto schéma je snáze pochopitelné než předchozí schéma, které používalo zvláštní hodnotu vnitřní káva a nebrání tomu, aby někdo rozšířil maximální hodnotu pro vnitřní káva.

Použití konstant

Dalším pravidlem, které je třeba dodržovat při vytváření polí, je použití konstant (statické konečné proměnné) pro konstantní hodnoty, které jsou předávány, vráceny z nebo použity v rámci metod. Pokud metoda očekává jednu z konečných sad konstantních hodnot v jednom ze svých parametrů, definování konstant pomáhá klientským programátorům lépe pochopit, co je třeba v tomto parametru předat. Podobně, pokud metoda vrací jednu z konečné sady hodnot, deklarováním konstant je pro klientské programátory jasnější, co mohou očekávat jako výstup. Například je snazší to pochopit:

if (cup.getSize () == CoffeeCup.TALL) {} 

než je to pochopit:

if (cup.getSize () == 1) {} 

Měli byste také definovat konstanty pro interní použití metodami třídy - i když se tyto konstanty nepoužívají mimo třídu - takže je snazší je pochopit a změnit. Použití konstant zvyšuje flexibilitu kódu. Pokud si uvědomíte, že jste nesprávně vypočítali hodnotu a nepoužili jste konstantu, budete muset projít svým kódem a změnit každý výskyt pevně zakódované hodnoty. Pokud jste použili konstantu, budete ji muset změnit pouze tam, kde je definována jako konstanta.

Konstanty a kompilátor Java

Užitečné je vědět o kompilátoru Java, že se statickými koncovými poli (konstantami) zachází jinak než s jinými druhy polí. Odkazy na statické konečné proměnné inicializované na časovou konstantu kompilace jsou vyřešeny v době kompilace na místní kopii konstantní hodnoty. To platí pro konstanty všech primitivních typů a typu řetězec java.lang.

Normálně, když vaše třída odkazuje na jinou třídu - řekněme třídu java.lang.Math - kompilátor Java umístí symbolické odkazy na třídu Matematika do souboru třídy pro vaši třídu. Například pokud vyvolá metoda vaší třídy Math.sin (), váš soubor třídy bude obsahovat dva symbolické odkazy na Matematika:

  • Jeden symbolický odkaz na třídu Matematika
  • Jeden symbolický odkaz na Matematikaje hřích() metoda

K provedení kódu obsaženého ve vaší třídě, na který se odkazuje Math.sin (), JVM by musel načíst třídu Matematika vyřešit symbolické odkazy.

Pokud však váš kód odkazoval pouze na statickou proměnnou konečné třídy PI deklarováno ve třídě Matematika, by kompilátor Java neumístil žádný symbolický odkaz Matematika v souboru třídy pro vaši třídu. Místo toho by jednoduše umístil kopii doslovné hodnoty Math.PI do třídního souboru vaší třídy. Chcete-li spustit kód obsažený ve vaší třídě, která používá Math.PI konstanta, JVM by nemusel načíst třídu Matematika.

Výsledkem této funkce kompilátoru Java je, že JVM nemusí pracovat o nic tvrději, aby používal konstanty, než je tomu v případě použití literálů. Preferování konstant před literály je jedním z mála návrhových pokynů, které zvyšují flexibilitu programu bez rizika jakéhokoli zhoršení výkonu programu.

Tři druhy metod

Zbývající část tohoto článku pojednává o technikách návrhu metody, které se zabývají daty, která metoda používá nebo upravuje. V této souvislosti bych chtěl identifikovat a pojmenovat tři základní typy metod v programech Java: užitková metoda the metoda zobrazení stavua metoda změny stavu.

Metoda nástroje

Metoda obslužného programu je metoda třídy, která nepoužívá ani nemění stav (proměnné třídy) své třídy. Tento druh metody jednoduše poskytuje užitečnou službu související s jeho třídou objektu.

Některé příklady metod obslužných programů z rozhraní Java API jsou:

  • (Ve třídě Celé číslo) public static int toString (int i) - vrátí nový Tětiva objekt představující zadané celé číslo v radix 10
  • (Ve třídě Matematika) public static native double cos (double a) - vrátí trigonometrický kosinus úhlu

Metoda stavového pohledu

Metoda zobrazení stavu je metoda třídy nebo instance, která vrací určitý pohled na vnitřní stav třídy nebo objektu, aniž by se tento stav změnil. (Tento druh metody bezostyšně ignoruje Heisenbergův princip nejistoty - viz Zdroje, pokud potřebujete osvěžení tohoto principu.) Metoda stavového pohledu může jednoduše vrátit hodnotu proměnné třídy nebo instance, nebo může vrátit hodnotu vypočítanou z několik proměnných třídy nebo instance.

Některé příklady metod zobrazení stavu z rozhraní Java API jsou:

  • (Ve třídě Objekt) public String toString () - vrací řetězcovou reprezentaci objektu
  • (Ve třídě Celé číslo) veřejný bajt byteValue () - vrací hodnotu Celé číslo objekt jako bajt
  • (Ve třídě Tětiva) public int indexOf (int ch) - vrátí index v řetězci prvního výskytu zadaného znaku

Metoda změny stavu

Metoda změny stavu je metoda, která může transformovat stav třídy, ve které je metoda deklarována, nebo, je-li použita metoda instance, objekt, na který je vyvolána. Když je vyvolána metoda změny stavu, představuje to „událost“ pro třídu nebo objekt. Kód metody "zpracovává" událost, což potenciálně mění stav třídy nebo objektu.

Některé příklady metod změny stavu z rozhraní Java API jsou:

  • (Ve třídě StringBuffer) public StringBuffer append (int i) - připojí řetězcové vyjádření int argument k StringBuffer
  • (Ve třídě Hashtable) public synchronized void clear () - vymaže Hashtable takže neobsahuje žádné klíče
  • (Ve třídě Vektor) public final synchronized void addElement (Object obj) - přidá zadanou komponentu na konec souboru Vektor, čímž se jeho velikost zvětšila o jednu

Minimalizace spojování metod

Vyzbrojeni těmito definicemi užitečnosti, zobrazení stavu a metod změny stavu jste připraveni na diskusi o propojení metod.

Při navrhování metod by jedním z vašich cílů měla být minimalizace spojka - míra vzájemné závislosti mezi metodou a jejím prostředím (jiné metody, objekty a třídy). Čím méně vazby existuje mezi metodou a jejím prostředím, tím nezávislejší je tato metoda a tím flexibilnější je návrh.

Metody jako transformátory dat

Abychom porozuměli spojování, pomáhá to uvažovat o metodách čistě jako transformátorech dat. Metody přijímají data jako vstup, provádějí operace s těmito daty a generují data jako výstup. Stupeň vazby metody je určen především tím, kde získává svá vstupní data a kam dává svá výstupní data.

Obrázek 1 ukazuje grafické znázornění metody jako datového transformátoru: Diagram toku dat ze strukturovaného (ne objektově orientovaného) designu.

Vstup a výstup

Metoda v Javě může získat vstupní data z mnoha zdrojů:

  • Může vyžadovat, aby volající při vyvolání specifikoval své vstupní údaje jako parametry
  • Může zachytit data z libovolných proměnných přístupné třídy, jako jsou například vlastní proměnné třídy dané třídy nebo jakékoli proměnné třídy přístupné jiné třídy
  • Pokud se jedná o instanční metodu, může zachytit proměnné instance z objektu, na který byla vyvolána

Podobně může metoda vyjádřit svůj výstup na mnoha místech:

  • Může vrátit hodnotu, buď primitivní typ, nebo odkaz na objekt
  • Může měnit objekty, na které odkazují odkazy předávané jako parametry
  • Může změnit jakékoli proměnné třídy vlastní třídy nebo jakékoli proměnné třídy přístupné jiné třídy
  • Pokud se jedná o instanční metodu, může změnit libovolné proměnné instance objektu, na který byla vyvolána
  • Může vyvolat výjimku

Všimněte si, že parametry, návratové hodnoty a vyvolané výjimky nejsou jedinými druhy vstupů a výstupů metod uvedených ve výše uvedených seznamech. Proměnné instance a třídy se také považují za vstup a výstup. Z objektově orientovaného pohledu se to může zdát neintuitivní, protože přístup k proměnným instance a třídy v Javě je „automatický“ (metodě nemusíte nic výslovně předávat). Při pokusu o měření vazby metody je však nutné se podívat na druh a množství dat použitých a upravených kódem, bez ohledu na to, zda byl přístup kódu k těmto datům „automatický“.

Minimálně spojené užitné metody

Nejméně spojená metoda, která je v Javě možná, je metoda obslužného programu, která:

  1. Bere vstup pouze ze svých parametrů
  2. Vyjadřuje svůj výstup pouze prostřednictvím svých parametrů nebo návratové hodnoty (nebo vyvoláním výjimky)
  3. Přijímá jako vstup pouze data, která jsou skutečně potřebná metodou
  4. Vrátí jako výstup pouze data, která jsou skutečně vytvořena metodou

Dobrá užitečná metoda

Například metoda convertOzToMl () zobrazené níže přijímá int jako jeho jediný vstup a vrací int jako jediný výstup:

$config[zx-auto] not found$config[zx-overlay] not found