1.2. Objektorientētā programmēšana

Mērķi

  • Definēt OO modelēšanas jēdzienus: abstrakcija (abstraction), iekapsulēšana (encapsulation) un pakotne (package)
  • Noskaidrot, kāpēc Javas aplikāciju kodu var atkalizmantot
  • Definēt Javas jēdzienus klase (class), loceklis (member), atribūts (attribute), metode (method), konstruktors (constructor) un pakotne (package)
  • Izmantot piekļuves modifikatorus private un public saskaņā ar iekapsulēšanas vadlīnijām
  • Izsaukt metodi uz konkrēta objekta
  • Javas programmā prast atrast package komandu, import komandas, klases, metodes un atribūtus kā arī konstruktorus
  • Lietot Javas lietojumprogrammu interfeisu (API) elektronisko dokumentāciju

Kas ir objektorientēta projektēšana (design)?

Ievadjautājumi

  • Ko nozīmē programmatūras analīze un projektēšana?
  • Kā projektēšana ir saistīta ar koda atkalizmantošanu?
  • Kādas ir Javas īpašības, kuras padara to par objektorientētu valodu?
  • Raksturojiet jēdzienu objektorientēts (object-oriented).

Programminženierija

Programmatūras evolūcijas galvenie posmi

Programmatūras veidošanā kopš pirmajām programmēšanas valodām ir centušies ievies atkalizmantojamu kodu (noslēpt mašīnvalodu sarežģītību un piedāvāt funkciju izsaukumus). Procedūras un funkcijas tika apkopotas bibliotēkās.

Agrīnās bibliotēkas manipulēja ar datiem kā ar atvērtām struktūrām (C valodas struct). Bibliotēkas izstrādātājs tad nevar noslēpt datu iekšējo uzbūvi. Arī bibliotēkas lietotājs bieži kļūst atkarīgs no datu struktūru iekšējām detaļām. 1980-to gadu beigās izplatījās C++ un objektorientētā programmēšana. Tajā procedūras asociēja ar datu struktūru un noslēpa implementācijas detaļas. Šādu datu un to apstrādes metožu kombināciju nosauca par klasi (class).

Mūsdienās funkciju bibliotēku analogs ir klašu bibliotēkas un API specifikācijas un karkasi (framework), kur lietotājs var izvēlēties dažādu piegādātāju implementācijas, tādējādi palielinot sava programmnodrošinājuma elastību un veiktspēju.

Javas platformu nemitgi papildina jauni API un specifikācijas, piemēram, Swing, JavaBeans, JDBC un daudzi citi.

Analīzes un projektēšanas fāze

  • Analīze apraksta ko sistēmai vajadzētu darīt. Mēdz modelēt reālo pasauli - aktierus un viņu darbības, kā arī implementējamos objektus un viņu uzvedību.
  • Projektējums apraksta sistēma to dara. To dara, modelējot attiecības un mijiedarbību starp objektiem un aktieriem sistēmā, kā arī atrodot noderīgas abstrakcijas, kuras uzdevumu var vienkāršot.

Abstrakcija

  • Funkcijas - vienreiz uzrakstītu algoritmu var lietot daudzās situācijās
  • Objekti - savstarpēji saistītus atribūtus un to uzvedību var grupēt klasē
  • Ietvari/karkasi un API - lielas objektu grupas, kas nodrošina sarežģītu mijiedarbību

    Ietvarus var lietot oriģinālajā veidā un var arī paplašināt, lai paplašinātu tajos iebūvēto uzvedību ar jaunām iezīmēm. (Open-closed princips apgalvo, ka pielāgojumu klasēs, kuras manto no ietvara klasēm, nedrīkst mainīt ietvara klases uzvedību; to drīkst vienīgi paplašināt. No šejienes ir teiciens: Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. (Martin Fowler)

Sadalīšana sastāvdaļās (decomposition)

Priekšrocības, ja programmu dala mazākās daļās:

  • Darba dalīšana: var paātrināt izstrādi, sadalot cilvēkiem
  • Atkalizmantošana: dažus gabalus, kuri ir kopīgi vairākām programmām var izgatavot vienreiz un lietot daudzkur
  • Modulāra analīze: Katru daļu var atsevišķi pārbaudīt
  • Izmaiņu lokalizējamība: Izmaiņa parasti skar tikai dažas no daļām

Dijkstras arguments: Ja c ir varbūtība, ka viena daļa ir uzprogrammēta pareizi un daļu pavisam ir N, tad varbūtība, ka visas N daļas kopā strādās pareizi ir cN. Šī varbūtība tiecas uz 0, ja N pieaug. (Dahl, Dijskstra, Hoare. "Structured Programming", 1972. Academic Press)

Pirmā dekompozīcijas pieeja - "top-down design"

Viens pulksteņmeistars gatavo pulksteni vienā paņēmienā, bet otrs to veido pa moduļiem un moduļus pēc tam saliek kopā. Abiem pulksteņmeistariem reizēm zvana telefons - un attiecīgajā brīdī veidotais nepabeigtais salikums izjūk. Priekšrocības ir tam pulksteņmeistaram, kurš strādā hierarhiski - pa moduļiem. (Simon. "The Architecture of Complexity")

Lielu sistēmu var būvēt, atkārtojot rekursīvi šādus soļus:

  • Ja daļa, kura mums jāuzbūvē ir jau pieejama (teiksim, 1 operators), tad izvēlamies to
  • Pretējā gadījumā sadalām uzdevumu daļās, uzprogrammējam šīs daļas, un saliekam kopā

Šāda pieeja ir izplatīta struktūrprogrammēšanā, tā pazīstama arī kā "top-down design" metode; līdzīgus spriešanas paņēmienus lietoja jau 17.gs. filozofs un matemātiķis R.Dekarts. (Divide each difficulty into as many parts as is feasible and necessary to resolve it., sk. Wikiquote.

Uzlabota dekompozīcijas pieeja - mijietekmes samazināšana

Patvaļīgai daļās dalīšanai, ko realizē projektēšanas pieeja "no augšas uz leju" ir būtisks trūkums - tā neatbild uz jautājumu, vai pirmais dalījums ir labs, pirms neesam visu uzbūvējuši.

Cenšamies paturēt daļas vienādā abstrakcijas līmenī un samazināt mijietekmi (coupling) sastāvdaļu starpā - lai izmaiņas varētu veikt pēc iespējas neatkarīgi dažādajās daļās. Šo sauc arī par modularitātes principu objektorientētā projektēšanā.

Klases kā objektu projektējumi (blueprint)

Ražošanā projektējums ir apraksts, no kura konstruē fiziskos objektus. Programmatūras izstrādē klase ir projektējums objektiem:

  • Klase apraksta datus, kurus ietver katrs objekts.
  • Klase apraksta uzvedību, kura piemīt katram objektam.

    T.i. objektorientētā programmēšanā pastāv objekti, kuri modelē kaut kādas reālās pasaules norises datora atmiņā (konkrētie objekti ir, piemēram, stringi, saraksti, datumi, apdrošināšanas polises, utml.). Savukārt klases ir šo konkrēto objektu abstrakcijas, kuras raksturo visiem noteikta veida objektiem (t.i. stringiem, sarakstiem, datumiem, polisēm, utml.) kopīgās īpašības un atļautās darbības ar tiem.

3 objektorientētu programmēšanas valodu pamatiezīmes

Javas tehnoloģijā klases atbalsta šādas 3 objektorientētās programmēšanas pamatiezīmes:

Javas sintakse objektorientācijai

Klašu deklarēšana Javā

Javas klases sintakse:

<modifiers> class <class_name> [<attribute_declarations>] [<constructor_declarations>] [<method_declarations>]

Piemērs:

public class Vehicle {
    private double maxLoad;
    public void setMaxLoad(double value) {
        maxLoad = value;
    }
}

Atribūtu deklarēšana

Atribūta sintakse:

<modifiers> <type> <name>;

Piemērs:

public class Foo {
  private int x;
  private float  y = 10000.0F;
  private String name = "Bates Motel";
}

Metožu deklarēšana

Metožu deklarēšanas vispārīgā sintakse:

<modifiers> <return_type> <name> ([<argument_list>]) [<statements>]

Piemērs - metodes getWeight() un setWeight():

public class Dog {
  private int weight;
  public int getWeight() {
    return weight;
  }
  public void setWeight(int newWeight) {
    weight = newWeight;
  }
}

Piekļuve objekta locekļiem

  • "Punkta operators": <object>.<member>
  • Ar šo piekļūst gan atribūtiem, gan metodēm

Piemērs:

d.setWeight(42);
d.weight = 42;  // atļauts vienīgi tad, ja "weight" ir "public"

Iekapsulēšanas piemērs - klase MyDate

Aplūkosim šādu (sliktā, neobjektorientētā veidā projektētu) Javas klasi:

Struktūra 'MyDate' bez iekapsulēšanas

Klienta programmai (t.i. jebkuram kodam, kurš no ārpuses izmanto klasi MyDate) ir tieša pieeja šīs klases iekšējiem datiem:

MyDate d = new MyDate(); 
d.day = 32; // neesošs datums
d.month = 2; d.day = 29; // grūtāk noskaidrot datuma esamību
d.day = d.day + 1; // nepārbauda mēneša beigas

Uzlabots risinājums:

Struktūra 'MyDate' ar 'set' un 'get' metodēm

Klienta programmai jālieto setXxx()/getXxx() metodes, lai piekļūtu iekšējiem datiem:

MyDate d = new MyDate(); 
d.setDay(32); // nederīga diena, atgriež "false"
d.setMonth(2);
d.setDay(30); // intervāliem atbilst, bet aplami, 
// setDay atriž vērtību "false"
d.setDay(d.getDay() + 1); // atgriež "false", ja ir mēneša beigas

Klases projektējums ar iekapsulēšanu

  • Noslēpj klases implementācijas detaļas
  • Lietotājs spiests lietot interfeisu, lai piekļūtu datiem
  • Padara kodu vieglāk uzturamu

Lietojot iekapsulēšanu, klases MyDate iekšējo uzbūvi var mainīt (vienu privāto atribūtu vietā ieviešot citus utml.), bet ja nebūs izmainījusies publisko metožu uzvedība, šīs izmaiņas klases lietotāji nepamanīs.

Modificēta 'MyDate' implementācija

Konstruktori

Konstruktoru deklarēšana

Konstruktora sintakse

<modifier> <class_name> ([<argument_list>]) [<statements>]

Piemērs

1  public class Dog {
2    private int weight;
3  
4    public Dog() {
5       weight = 42;
6    }
7  
8    public int getWeight() {
9      return weight;
10    }
11    public void setWeight(int newWeight) {
12      weight = newWeight;
13    }
14  }
1  public class TestDog {
2      public static void main(String[] args) {
3          Dog d = new Dog();
4          d.setWeight(42);
5          System.out.println("Dog d's weight is " + d.getWeight());
6      }
7  }

Noklusētais konstruktors

  • Katrai klasei ir vismaz viens konstruktors
  • Ja izstrādātājs neuztaisa nevienu konstruktoru, automātiski izveidojas noklusētais konstruktors bez argumentiem un ķermeņa.
  • Dod iespēju veidot objekta instances ar new Xxx() neradot nepieciešamību pēc atklāta konstruktora.

Izejas faila struktūra un pakotnes

Javas izejas faila (*.java) struktūra

Ikviens Javas fails ir ar sekojošu struktūru - vispirms pakotnes deklarācija, tad importa deklarācijas, visbeidzot viena (izņēmuma gadījumos vairākas) klašu deklarācijas. Katrā Javas failā var deklarēt tikai vienu "public" klasi, kuras vārdam ir jāsakrīt ar *.java faila nosaukumu.

[<package_declaration>] [<import_declarations>] <class_declaration>+

Piemērs: fails VehicleCapacityReport.java:

package shipping.reports; 
  
import shipping.domain.*; 
import java.util.List; 
import java.io.*; 
  
public class VehicleCapacityReport { 
    private List  vehicles; 
    public void generateReport(Writer output) {...} 
} 

Programmatūras pakotnes (package)

  • Pakotnes izmanto lielu programmatūras sistēmu organizēšanai
  • Pakotnes satur klases un apakšpakotnes
UML diagrammas ar pakotnēm

Apakšpakotnes direktoriju struktūrā atbilst apakšdirektorijām. Tomēr katra pakotne ir pilnīgi neatkarīga no citām pakotnēm, neatkarīgi no to savstarpējā novietojuma direktoriju kokā. (Komandas package un import apstrādā individuāli, piemēram pakotnes java.awt un java.awt.event). Pakotņu un apakšpakotņu struktūra kalpo, lai milzīgajā klašu nosaukumu saimē ieviestu vārdapgabalus (namespace), t.i. logjiskas "vietas", kuru ietvaros klašu nosaukumi noteikti ir unikāli.

Vārdapgabali ir izplatīti arī ikdienas dzīvē. Piemēram, ielas nosaukums "Rīgas iela" nav viennozīmīgs - šādas ielas ir Sabilē, Jēkabpilī, Saldū, Salaspilī, Valmierā, u.c. Toties, līdzko izvēlamies vārdapgabalu (konkrētu pilsētu), tad visi ielu nosaukumi šīs pilsētas ietvaros ir unikāli.

Komanda package

Pakotnes komandas sintakse:

package <top_pkg_name>[.<sub_pkg_name>]*;

Piemērs:

package shipping.reports; 
  • Pakotnes deklarācijai (ja tāda ir) jābūt Javas izejas faila pašā sākumā.
  • Pakotnes deklarācija drīkst būt tikai viena.
  • Ja pakotne nav deklarēta, klase pieder pie "noklusētās pakotnes". Šādas noklusētās pakotnes nav ieteicams lietot nekādās Javas programmās. Tas attaisnojams vienīgi īslaicīgos izmēgjinājumos, ko ir ērtāk kompilēt no DOS komandrindas - īsāks pieraksts, jo nekur nav jānorāda pilns pakotnes nosaukums.
  • Pakotnes nosaukums sastāv no vairākiem identifikatoriem, kurus atdala punkti.

import komanda import

Importa komandas sintakse:

import <pkg_name>[.<sub_pkg_name>].<class_name>;

VAI

import <pkg_name>[.<sub_pkg_name>].*;

Piemēri:

import shipping.domain.*;
import java.util.List;
import java.io.*;
  • Importa komandas ir pirms klašu deklarācijām
  • Pasaka kompilatoram, kur atrast klases, kuras minētas programmas tekstā

Pakotnes un programmu izvietojums pa direktorijām

  • Pakotnēs ietilpstošās sakompilētās programmas glabājas direktoriju kokā atbilstoši pakotnes nosaukumam
  • Piemērs: pakotnes pārvadājumu uzņēmuma aplikācijai:
Pakotnes un atbilstošās direktorijas

Direktoriju struktūra izstrādes laikā

Aptuvens izstrādes direktoriju struktūras piemērs

Kompilācija, lietojot karodziņu -d:

cd  JavaProjects/BankPrj/src
javac  -d  ../class  banking/domain/*.java

Direktoriju struktūra darbināšanas laikā

  • JVM meklē izpildāmo pakotni un tās klasi (t.sk. klases "noklusētajā" pakotnē) savā CLASSPATH mainīgajā.
  • CLASSPATH var saturēt gan direktorijas, gan JAR un ZIP arhīvus
  • Ja CLASSPATH nav uzstādīts, tad tas vienāds ar tekošo direktoriju - "."
  • CLASSPATH var uzstādīt kā sistēmas vides mainīgo vai ar komandrindu:
    set CLASSPATH=.;c:\JavaProjects\BankPrj\class;c:\JavaProjects\lib\biblioteka.jar
    java banking.GUI.BankingMain
    
  • CLASSPATH var nodot JVM interpretatoram arī pēc karodziņa -classpath:
    java -classpath 
        c:\JavaProjects\BankPrj\class;c:\JavaProjects\lib\biblioteka.jar 
                banking.GUI.BankingMain
    

    (šo komandu raksta vienā rindā)

  • Ja kāda bibliotēka ir vajadzīga ļoti daudzkur un nevēlamies norādīt tās CLASSPATH konkrētajā datorā, tad izpildāmo JAR failu var iekopēt C:\Program Files\Java\jdk1.6.0\jre\lib\ext direktorijā.

Nobeigums

Terminoloģijas atkārtojums

  • Klase - Izejas koda līmenī rakstīts projektējums izpildes laika objektiem
  • Objekts - klases atsevišķs iemiesojums jeb instance
  • Atribūts - datu elements, kurš pieder objektam (arī "datu loceklis", "objekta mainīgais", "datu lauks")
  • Metode - objekta uzvedību noteicošs elements (arī "funkcija", "procedūra")
  • Konstruktors - metodei līdzīgs izpildāms koda fragments, ar kuru inicializē jaunu objektu
  • Pakotne - klašu (un apakšpakotņu) sagrupējums

Kā lietot Java API dokumentāciju

  • HTML failu kopums, kurš satur informāciju par API
  • Pakotnē ir hipersaites uz informāciju par tajā ietilpstošajām klasēm
  • Klases dokumentācijā norādīta klases hierarhija, klases apraksts, tai piederošo mainīgo (atribūtu) saraksts, konstruktoru un metožu saraksts.

API dokumentācijas paraugs

Javas API dokumentācija pārlūkprogrammā

Jautājumi paškontrolei

  • Definējiet modelēšanas jēdzienus: abstrakcija (abstraction), iekapsulēšana (encapsulation) un pakotnes (package)
  • Kādos gadījumos Javas tehnoloģiju kodu var atkārtoti izmantot
  • Definējiet jēdzienus klase (class), loceklis (member), atribūts (attribute), metode (method), konstruktors (constructor) un pakotne (package)
  • Izmantojiet atbilstošus pieejas modifikatorus private un public saskaņā ar iekapsulēšanas vadlīnijām
  • Izsaukt metodi uz dotā objekta

    Patvaļīgā Javas programmas izejas tekstā atrodiet šādas lietas:

  • package komandu
  • import komandas
  • Klases, metodes un atribūtus
  • Konstruktorus

Izmantojot Javas API dokumentāciju, atrodiet metodes klasei java.lang.String un java.lang.Object.

Diskusiju tēmas

  • Vai Jūs savā programmēšanas praksē veltiet pietiekami daudz laika analīzei un projektēšanai?
  • Kādi pakotņu nosaukumi parādās jau eksistējošās Javas aplikācijās, ar kurām Jūs strādājat; kādus nosaukumus Jūs izvēlēsieties saviem projektiem?
  • "Doclets" jeb dokletu tehnoloģija ļauj programmētājam pašam kontrolēt API dokumentācijas utml. statisku failu ģenerēšanu no Javas izejas tekstiem un to komentāriem. Kādiem mērķiem varētu lietot dokletus?