Teknologian jakaminen

[Johdatus JAVAan] Päivä 15 - Käyttöliittymä

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

[Johdatus JAVAan] Päivä 15 - Käyttöliittymä



Miksi käyttöliittymä on olemassa?
Kuten me kaikki tiedämme, periytyminen tarkoittaa alaluokan yleisten abstraktioiden erottamista emoluokkaan jakoko järjestelmän yhteisyysKoodin tallentamiseksi on kuitenkin myös tilanne: useimmilla alaluokilla on tietty yhteisyys, mutta vain muutamalla alaluokilla ei ole tätä ominaisuutta, niin jos tämä yhteisyys kirjoitetaan yläluokkaan, se periytyy, mikä ei ole järkevää nämä muutamat alakategoriat.
Esimerkiksi: kissat ja koirat voivat uida, mutta kanit eivät tällä hetkellä, jos "uinti" on määritelty vanhempainluokassa, on kohtuutonta, että kissat ja koirat kirjoittavat omat uintimenetelmänsä Vastaavasti se voi tapahtua. Kahden menetelmän kirjoitusmuoto ja nimeäminen ovat epäjohdonmukaisia. Siksi meidän on määriteltävä uusi käsite rajoittaaksemme "uinti"-menetelmien kirjoitusspesifikaatioita näissä kahdessa alaluokassa.Tässä vaiheessa voimme määritellä "uinnin"käyttöliittymä, määritä käyttöliittymässä abstrakti menetelmä swim() ja anna sitten kissojen ja koirien muodostaa yhteys tähän käyttöliittymään varmistaaksesiKoodin yhdistäminen
Yhteenvetona käyttöliittymä on asääntö, kun meidän on määritettävä säännöt useille luokille samanaikaisesti, meidän on käytettäväkäyttöliittymä

1. Käyttöliittymä on abstraktio "käyttäytymisestä"

Käyttöliittymä ei edusta tietyntyyppistä asiaa, käyttöliittymä edustaasääntö, joten rajapinnat voidaan välittää parametreina menetelmille.
"Onpa kyseessä kuorma-auto, kolmipyöräinen auto tai työvoima, niin kauan kuin se pystyy liikkumaan, siitä on hyötyä."

搬家(车的对象);
  • 1
搬家(搬家公司);
  • 1
public interface 运输 {
	...
}
  • 1
  • 2
  • 3
public void 搬家(运输的接口 c) {
	...
}
  • 1
  • 2
  • 3

2. Liitäntöjen määrittely ja käyttö

  • Liitännät määritellään avainsanarajapinnalla.
public interface 接口名 {}
  • 1
  • Liitäntöjä ei voi ilmentää, mikä tarkoittaa, että rajapintoja ei voida käyttää objektien luomiseen.
  • Ero käyttöliittymän ja luokan välillä onsaavuttaaSuhteet esitetään implements-avainsanalla.
public class 类名 implements 接口名 {}
  • 1
  • Rajapinnan alaluokka (toteutusluokka) joko ohittaa kaikki rajapinnan abstraktit menetelmät tai on itse abstrakti luokka.
  • Toteutussuhde rajapintojen ja luokkien välillä voi ollaYksittäinen toteutus, voi myösUseita toteutuksia
public class 类名 implements 接口名1 , 接口名2 {}
  • 1
  • Toteutusluokka voi toteuttaa useita rajapintoja samalla, kun se perii luokan.
public class 类名 extends 父类 implements 接口名1 , 接口名2 {}
  • 1

harjoitella: Kirjoita standardi Javabean-luokkia rajapinnoilla ja abstrakteilla luokilla.

 青蛙		属性:名字,年龄		行为:吃虫子,蛙泳
 狗			属性:名字,年龄		行为:吃骨头,狗刨
 兔子		属性:名字,年龄		行为:吃胡萝卜
  • 1
  • 2
  • 3

Kirjoita ensin yläluokka, koska kolme alaluokkaa syövät eri asioita, voit määrittää eat():n abstraktiksi menetelmäksi:

package oopInterface;

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

Kirjoita käyttöliittymä Swim:

package oopInterface;

public interface Swim {
    public abstract void swim();
}
  • 1
  • 2
  • 3
  • 4
  • 5

Kun kirjoitat sammakkoluokkaa, kiinnitä huomiota pääluokan perimiseen ja emoluokan abstraktin metodin uudelleenkirjoittamiseen sekä kiinnitä huomiota uimarajapinnan toteuttamiseen:

package oopInterface;

public class Frog extends Animal implements Swim {
    public Frog() {
        super();
    }

    public Frog(String name, int age) {
        super(name, age);
    }

    @Override
    public void swim() {
        System.out.println("青蛙在蛙泳。");
    }

    @Override
    public void eat() {
        System.out.println("青蛙在吃虫子。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Kun kirjoitat koiraluokkaa, kiinnitä huomiota emoluokan perimiseen ja emoluokan abstraktin menetelmän uudelleenkirjoittamiseen uimarajapinnan toteuttamiseksi:

package oopInterface;

public class Dog extends Animal implements Swim {

    public Dog(){
        super();
    }

    public Dog(String name, int age) {
        super(name,age);
    }
    @Override
    public void swim() {
        System.out.println("狗在狗刨。");
    }

    @Override
    public void eat() {
        System.out.println("狗在吃骨头。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Kun kirjoitat kaniluokkaa, huomaa, että sinun tarvitsee vain periä yläluokka ja ohittaa abstrakti menetelmä, eikä sinun tarvitse toteuttaa uimarajapintaa (et voi uida):

package oopInterface;

public class Rabbit extends Animal {

    public Rabbit() {
    }

    public Rabbit(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("兔子在吃胡萝卜。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Kirjoita testiluokat.

package oopInterface;

public class Test {
    public static void main(String[] args) {
        Frog f = new Frog("小绿",23);
        f.eat();
        f.swim();
        System.out.println(f.getName() + ", " + f.getAge());
        Dog d = new Dog("大D", 24);
        d.eat();
        d.swim();
        System.out.println(d.getName() + ", " + d.getAge());
        Rabbit r = new Rabbit("兔子", 30);
        r.eat();
        System.out.println(r.getName() + ", " + r.getAge());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3. Liittymän jäsenten ominaisuudet

  • Jäsenmuuttujat: Jäsenmuuttujat käyttöliittymässä voivat olla vainvakio, julkista staattista lopullista käytetään oletusarvoisesti (vaikka et kirjoittaisi sitä, se katsotaan automaattisesti tällä tavalla).
  • Rakentaja: käyttöliittymäEiRakennusmenetelmä.
  • Jäsenmenetelmät: Ennen JDK7:ää ne saattoivat olla vain abstrakteja menetelmiä.oletuksenaMuuntaja on julkinen abstrakti JDK8:n jälkeen, menetelmät, joissa on menetelmä rungot, voidaan määrittää rajapinnoissa JDK9:n jälkeen, yksityiset menetelmät voidaan määrittää rajapinnoissa.

4. Liitäntöjen ja luokkien välinen suhde

  • Luokkien välinen suhde: Periytyssuhde - vain yksi perintö, ei moniperintö, mutta monitasoinen periytyminen on mahdollista.
  • Luokkien ja rajapintojen välinen suhde: Toteutussuhde - se voidaan toteuttaa yksin tai useissa toteutuksissa, ja voit myös toteuttaa useita rajapintoja samalla kun perii luokkaa.
  • Liitäntöjen välinen suhde: perinnöllinen suhde - se voi olla yksiperäinen tai moniperintö.

Ilmoitus:
1. Kun luokka toteuttaa rajapinnan, sen tulee joko toteuttaa kaikki rajapinnan abstraktit menetelmät tai luokka itse on myös abstrakti luokka.
2. Luokka voi toteuttaa useita rajapintoja, jos se toteuttaa useita rajapintojakaikkiKaikki rajapinnassa toteutettavat abstraktit menetelmät on toteutettu.
3. Liitännät voidaan periä useista liitännöistä. Jos alirajapinta perii useita rajapintoja ja sen sitten toteuttaa toteutusluokka, toteutusluokan on yhdistettävä alirajapinta ja kaikki sen ylärajapinnat.kaikkiKaikki abstraktit menetelmät toteutetaan.

harjoitella: Kirjoita standardi Javabean-luokkia rajapinnoilla ja abstrakteilla luokilla.

乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
  • 1
  • 2
  • 3
  • 4
//Person类
package oopInterExp;

//因为直接创建顶层父类人的对象是没有意义的
//所以将其写为抽象类
public abstract class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
//Sporter类继承Person
package oopInterExp;

public abstract class Sporter extends Person {

    public Sporter(String name, int age) {
        super(name, age);
    }

    public Sporter() {
    }

    public abstract void learn();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//Coach类继承Person
package oopInterExp;

public abstract class Coach extends Person {

    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    public abstract void teach();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//SpeakEnglish接口
package oopInterExp;

public interface SpeakEnglishInter {
    public abstract void speakEnglish();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
//PingPongSporter继承Sporter,实现SpeakEnglish
package oopInterExp;

public class PingPongSporter extends Sporter implements SpeakEnglishInter {

    public PingPongSporter(String name, int age) {
        super(name, age);
    }

    public PingPongSporter() {
    }

    @Override
    public void learn() {
        System.out.println("学习乒乓球。");
    }

    @Override
    public void speakEnglish() {
        System.out.println("乒乓球运动员在说英语。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
//PingPongCoach继承Coach,实现SpeakEnglish
package oopInterExp;

public class PingPongCoach extends Coach implements SpeakEnglishInter {
    public PingPongCoach() {
    }

    public PingPongCoach(String name, int age) {
        super(name, age);
    }

    @Override
    public void teach() {
        System.out.println("教乒乓球。");
    }

    @Override
    public void speakEnglish() {
        System.out.println("乒乓球教练在说英语。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
//BasketballSporter继承Sporter
package oopInterExp;

public class BasketballSporter extends Sporter {

    public BasketballSporter(String name, int age) {
        super(name, age);
    }

    public BasketballSporter() {
    }

    public void learn() {
        System.out.println("学篮球。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
//BasketballCoach继承Coach
package oopInterExp;

public class BasketballCoach extends Coach {

    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        super(name, age);
    }

    public void teach() {
        System.out.println("教篮球。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

5. Uusia menetelmiä käyttöliittymässä

5.1 Uudet menetelmät JDK8-aloitusrajapinnassa

Ennen JDK7:ää rajapinnoissa voitiin määritellä vain abstrakteja menetelmiä.
JDK8:n uusi ominaisuus on: menetelmät, joissa on metodikappaleita, voidaan määritellä käyttöliittymässä (voi määritelläoletuksenamenetelmä taistaattinenmenetelmä).
JDK9:n uusi ominaisuus on: se voidaan määritellä käyttöliittymässäyksityinenmenetelmä.

5.1.1 Oletusmenetelmät liitännöissä

Rajapinnassa määritellään menetelmät, joissa on menetelmäkappaleita, pääasiassa vartenKäyttöliittymän päivitysKoska käyttöliittymä ei voi olla muuttumaton, siihen on lisättävä uusia menetelmiä päivittämistä varten. Jos nämä menetelmät ovat abstrakteja menetelmiä, nämä menetelmät on tällä hetkellä toteutettava toteutusluokassa, mikä on erittäin hankalaa ja hankalaa. ei ole helppo synkronoida, jos Päivitys käyttää menetelmää, joten toteutusluokkaa ei tarvitse muokata lisää.

  • Oletusmenetelmien määrittämiseksi käyttöliittymässä on käytettävä avainsanoja oletuksena Hoito.
  • Muoto: julkinen oletuspalautusarvotyyppi menetelmän nimi (parametriluettelo) { }
  • Esimerkki: julkinen oletus void show() { }
  • Oletusmenetelmät eivät ole abstrakteja menetelmiä, eikä niitä tarvitse ohittaa. Mutta jos se kirjoitetaan uudelleen, oletusavainsana on poistettava uudelleenkirjoitettaessa.
  • julkinen voidaan jättää pois, mutta oletusarvoa ei voi jättää pois.
  • Jos useita rajapintoja on toteutettu ja useissa liitännöissä on samannimiset oletusmenetelmät, alaluokan on ohitettava menetelmä (ohjaamatta jättäminen aiheuttaa ristiriitoja).

Käyttöliittymä Inter1:

package oopInterface5;

public interface Inter1 {
    public abstract void method();
    public default void default_method() {
        System.out.println("Inter1接口中的默认方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Käyttöliittymä Inter2:

package oopInterface5;

public interface Inter2 {
    public default void default_method() {
        System.out.println("Inter2接口中的默认方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Kahdella oletusmenetelmällä on sama nimi. Jos toteutusluokka toteuttaa nämä kaksi rajapintaa samanaikaisesti, tämä oletusmenetelmä on kirjoitettava uudelleen.

package oopInterface5;

public class InterImpl implements Inter1, Inter2 {

    @Override
    public void method() {
        System.out.println("抽象方法的实现");
    }

    @Override
    public void default_method() {
        System.out.println("重写接口中的默认方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Testiluokka:

package oopInterface5;

public class Test {
    public static void main(String[] args) {
            InterImpl ii = new InterImpl();
            ii.method();				//抽象方法的实现
            ii.default_method();		//重写接口中的默认方法
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
5.1.2 Staattiset menetelmät liitännöissä

JDK8 ja uudemmat sallivat staattisten menetelmien määrittämisen rajapinnoissa, joita on muokattava staattisilla.
käyttöliittymässästaattinen menetelmäMääritelmämuoto on:

  • julkinen staattinen palautusarvotyyppi menetelmän nimi (parametriluettelo) { }
  • Esimerkki: julkinen staattinen void show() { }

Huomioitavaa staattisista menetelmistä käyttöliittymissä:

  • Staattiset menetelmät voivat vain läpäistäKäyttöliittymän nimiKutsuttu, ei voida kutsua toteutusluokan nimen tai objektin nimen kautta.
  • julkinen voidaan jättää pois, staattista ei voi jättää pois.

Kirjoita käyttöliittymä:

package oopInterface6;

public interface Inter {
    public abstract void method();

    public static void static_method() {
        System.out.println("接口中的静态方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Kirjoita toteutusluokka Luokassa on myös staattinen menetelmä, jolla on sama nimi kuin käyttöliittymässä oleva menetelmä, mutta tämä ei ole ohittavaa, koska staattisia menetelmiä ei voi ohittaa:

package oopInterface6;

public class InteImpl implements Inter {
    @Override
    public void method() {
        System.out.println("重写接口中的抽象方法");
    }

    //这不叫重写
    public static void static_method() {
        System.out.println("我不是重写的Inter接口中的静态方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Mutta itse asiassa nämä kaksi ovat eri menetelmiä.

package oopInterface6;

public class Test {
    public static void main(String[] args) {
        InteImpl ii = new InteImpl();
        ii.method();						  //重写接口中的抽象方法
        Inter.static_method();               //调用接口中的静态方法
        InteImpl.static_method();           //调用实现类中的一个同名的静态方法
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.2 Uudet menetelmät JDK9-aloitusrajapinnassa

5.2.1 Määritä käyttöliittymässä yksityiset menetelmät
  • Muoto 1: yksityinen palautusarvon tyyppi menetelmän nimi (parametriluettelo) { }
  • Esimerkki 1: yksityinen void show() { }
  • Käyttö: Käytä oletusmenetelmää.
package oopInterface7;

public interface InterA {
    public default void show1() {
        System.out.println("show1开始执行");
        show3();
    }

    public default void show2() {
        System.out.println("show2开始执行");
        show3();
    }

    //普通的私有方法,给默认方法服务的
    private void show3() {
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • Muoto 2: yksityinen staattinen palautusarvotyyppi menetelmän nimi (parametriluettelo) { }
  • Esimerkki 2: yksityinen staattinen void method() { }
  • Käyttö: Tarjoile staattisia menetelmiä.
package oopInterface7;

public interface InterB {
    public static void show1() {
        System.out.println("show1开始执行");
        show3();
    }

    public static void show2() {
        System.out.println("show2开始执行");
        show3();
    }

    //普通的私有方法,给静态方法服务的
    private static void show3() {
        System.out.println("记录程序在运行过程中的各种细节,这里有100行代码。");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

6. Käyttöliittymän käyttö

1. Käyttöliittymä edustaa sääntöjä ja on käyttäytymisen abstraktio. Jos haluat luokan käyttäytyvän, anna luokan toteuttaa vastaava käyttöliittymä.
2. Kun menetelmän parametri on rajapinta, liitäntä voidaan välittääKaikki toteutusluokan objektit, tätä lähestymistapaa kutsutaan rajapinnan polymorfismiksi.

7. Sovittimen suunnittelukuvio

  • Design Pattern on joukko luokiteltuja ja luetteloituja koodisuunnittelukokemuksia, joita käytetään toistuvasti ja jotka useimmat ihmiset tietävät. Suunnittelumallien käytön tarkoituksena on käyttää koodia uudelleen, tehdä koodista muiden ymmärrettävämpi, varmistaa koodin luotettavuus ja ohjelmoida uudelleenkäytettävyys.

Sovittimilla voidaan yksinkertaistaa koodia ja välttää hankaluuksia, joita syntyy, kun käyttöliittymässä on liian monta abstraktia menetelmää ja meidän tarvitsee käyttää vain osaa niistä.
Kirjoitusvaiheet ovat yleensä:
1. Kirjoita väliluokka XXXAdapter vastaavan rajapinnan toteuttamiseksi.
2. Toteuta rajapinnassa abstraktien menetelmien tyhjät toteutukset.
3. Anna todellisen toteutusluokan periä väliluokka ja kirjoittaa vaaditut menetelmät uudelleen.
4. Jotta muut luokat eivät luo sovitinluokan objekteja, välisovitinluokka on koristeltu abstraktilla.

käyttöliittymä:

package AdapterDesignPattern;

public interface Inter {
    public abstract void method1();
    public abstract void method2();
    public abstract void method3();
    public abstract void method4();
    public abstract void method5();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Adapteriluokka:

package AdapterDesignPattern;

public abstract class InterAdapter implements Inter {
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }

    @Override
    public void method4() {

    }

    @Override
    public void method5() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

Toteutusluokka:

package AdapterDesignPattern;

public class InterImpl extends InterAdapter {
    //我需要用到哪个方法,就重写哪个方法就可以了
    @Override
    public void method5() {
        System.out.println("只要用第五个方法");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10