Java - dedicnost a interface

Dedicnost a interface si ukazeme na priklade, ktory predstavuje uzivatela a informacny system. Uzivatel sa prihlasuje do IS pomocou svojho id a hesla. IS potom rozhodne, ci daneho uzivatela prihlasi alebo nie. Uvazme nasledujucich uzivatelov: A IS bude mat metodu login, ktora bude mat ako parametre nejake id uzivatela a jeho heslo. Tato metoda zaroven vrati logicku hodnotu true, ak sa jedna o opravneneho uzovatela, v opacnom pripade vrati false.

Dedicnost

Najprv si ukazeme ako by sme riesili tuto ulohu najhlupejsim sposobom. A to tak ze vytvorime pre kazdeho uzivatela odpovedajucu triedu:
public class Administrator {

    private int id;
    private String name;
    private String password;
    
    public Administrator(long id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    
    public int getId() {
        return id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    // + nejake metody, ktore sluzia na administraciu IS
}
  
public class Student {

    private int id;
    private String name;
    private String password;
    
    public Student(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    
    public int getId() {
        return id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    // + nejake metody, ktore sluzia na prihlasovanie predmetov
}
  
public class Teacher {

    private int id;
    private String name;
    private String password;
    
    public Teacher(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    
    public int getId() {
        return id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    // + nejake metody, ktorym ucitel moze zadavat do ISu nove predmety
}  
  
A nas informacny system by vyzeral nasledujuco
public class InformationSystem {

    public boolean login(Administrator admin, String password) {
        // podla toho ako je IS implementovany rozhodne ci uzivatela prihlasi
    }
    
    public boolean login(Teacher teacher, String password) {
        // podla toho ako je IS implementovany rozhodne ci uzivatela prihlasi
    }
    
    public boolean login(Student student, String password) {
        // podla toho ako je IS implementovany rozhodne ci uzivatela prihlasi
    }
}
  
Prve coho si vsimneme je, ze triedy Administrator, Teacher a Student maju vela spolocneho. Taktiez implementacia triedy InformationSystem je velmi stupidna, pretoze pre kazdeho uzivatela je zvlast metoda. Predstavte si, ze budeme chciet aby nas IS umoznil pristup este aj inym uzivatelom, to by sme zase museli pridat novu metodu login :( Vychodiskom je prave dedicnost, t.j. vytiahneme z tried Administrator, Teacher a Student vlastnosti, ktore maju vsetci spolocne a vytvorime triedu User
public class User {

    private int id;
    private String name;
    private String password;
    
    public User(int id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
    
    public int getId() {
        return id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
}
  
A triedy Administrator, Teacher a Student budu iba rozsirovat triedu User. Na rozsirovanie (dedenie) sa pouziva klucove slovo extends, takze nase triedy budu vyzerat takto
public class Administrator extends User {
    
    public Administrator(int id, String name, String password) {
        super(id, name, password);
    }
    
    // + nejake metody, ktore sluzia na administraciu IS
    
    // napr. metody createNewUser(), removeUser() a pod.
}
  
public class Teacher extends User {
    
    public Teacher(int id, String name, String password) {
        super(id, name, password);
    }
    
    // + nejake metody, ktore sluzia na administraciu IS
    
    // napr. metody createNewCourse(), inputMarks() a pod.
}
  
public class Student extends User {
    
    public Student(int id, String name, String password) {
        super(id, name, password);
    }
    
    // + nejake metody, ktore sluzia na administraciu IS
    
    // napr. metody enrollInCourse(), enrollForExam() a pod.
}
  
A nasa trieda InformationSystem bude vyzerat nasledujuco
public class InformationSystem {

    public boolean login(User user, String password) {
        // podla toho ako je IS implementovany rozhodne ci uzivatela prihlasi
    }
    
}
Takato implementacia je uz ovela prehladnejsia a rozumnejsia. Vsimnite si ze metoda login uz bere ako parameter instanciu triedy User, avsak zobere aj dalsie instancie tried, ktore su potomkami (dedia, rozsiruju) triedy User. Treba spomenut, ze Java nepodporuje viacnasobnu dedicnost, to znamena, ze nejaka trieda moze vzdy rozirovat maximalne 1 triedu (ale rozhrani moze implementovat kolko len chce). Tiez si vsimnite klucoveho slova super, ktore znamena, ze sa odkazujeme na rodicovsku triedu. V nasom pripade volame rodicovsky konstruktor.

Doteraz sme sa stretli s viditelnostami atributov a metod iba private a public. Pri dedicnosti sa casto pouziva viditelnost protected, co znamena, ze dany atribut/metodu mozeme upravovat/prekryvat aj z potomkov nasej danej triedy. Tak ako mame tento nas priklad, tak viditelnost atributu password v triede User je private a preto v triede Administrator nemozeme nijako sa odkazovat na tento atribut. Ak vsak zmenime tuto vididelnost na protected (v triede User), tak v triede Administrator mozeme s tymto atributom veselo manipulovat. V pripade protected metody mozeme v potomkoch tuto metodu prekryt. To znamena, ze si mozeme napisat vlastny kod tejto metody, avsak metoda musi zachovat rovnaky nazov, rovnaky pocet parametrov a ich typy a tiez musi vratit rovnaky typ.

Interface

Dalej si este ukazeme priklad na interface. Zatial sme nikde neuviedli na zaklade coho nas informacny system rozhodne, ci uzivatela prihlasi alebo nie. To naznacuje, ze uzivatela ani moc nezaujima ako je to v ISe implementovane, ale ake metody musi uzivatel pouzit aby sa prihlasil, preto vytvorime interface pre IS.
public interface InformationSystem {
    
    boolean login(User user, String passowrd);
}
  

A vytvorime si nasledujuce informacne systemy
 

Tieto informacne systemy budu implementovat rozhranie InformationSystem, t.j. musia
mat metodu login. Vsimnite si ze v rozhrani nie je uvedena viditelnost tejto metody,
avsak pri konkretnej implementacii budeme pouzivat viditelnost public.

public class StupidIS implements InformationSystem {

    public boolean login(User user, String password) {
        return true;
    }
}
StupidIS vzdy prihlasi kazdeho uzivatela.
public class NormalIS implements InformationSystem {
    
    private String[] passwords;
    
    public NormalIS() {
        passwords = new String[3];
        passwords[0] = "admin";
        passwords[1] = "abc123";
        passwords[2] = "pa$$word";
    }

    public boolean login(User user, String password) {
        if(user.getId() >= passwords.length) {
            return false;
        }
        return passwords[user.getId()].equals(password);
    }
}
NormalIS uz ma v sebe ulozene nejake hesla pomocou ktorych kontroluje prihlasujuceho uzivatela. Teraz este upravime triedu User tak, ze pridame novu metodu
public boolean login(InformationSystem is) {
      return is.login(this, password);
}
Vsimnite si, ze tato metoda bere ako paramter InformationSystem, teda vsetky instancie, ktore implementuju toto rozhranie. Z toho vyplyva, ze vzdy ked trieda implementuje rozhranie, definujte jej typ pomocou rozhrania. Podobne, i ked nie v takej miere, plati aj pre dedicnost. Dalsie poznamky najdete v komentaroch v prilozenom projekte Demo.