Teknologian jakaminen

Suunnittelumalli Vastuuketjun malli

2024-07-12

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

1. Konsepti

Vastuuketjumalli: Vältä pyynnön lähettäjän ja vastaanottajan yhdistämistä, anna useille objekteille mahdollisuus vastaanottaa pyyntö, yhdistä nämä objektit ketjuun ja välitä pyyntö tätä ketjua pitkin, kunnes on olemassa kohde, joka käsittelee sen.Vastuuketjumalli on aobjektin käyttäytymismalli

2. Rakenne

Vastuullisuusketjumallirakenteen ydin on aabstrakti prosessori
Lisää kuvan kuvaus tähän

Kuten kuvasta näkyy, vastuuketjumallin rakennekaavio sisältää seuraavat kaksi roolia:
(1) Käsittelijä (abstrakti käsittelijä): Se määrittelee rajapinnan pyyntöjen käsittelyä varten ja on yleensä suunniteltu abstraktiksi luokkaksi. Koska eri betonikäsittelijät käsittelevät pyyntöjä eri tavalla, niissä määritellään abstraktit pyyntöjen käsittelymenetelmät.Koska jokaisen prosessorin alainen on edelleen prosessori, abstraktissa prosessorissa oleva objekti (seuraaja rakennekaaviossa) määritellään abstraktissa prosessorissa sen alaisena.Lainata . Tämän viittauksen avulla käsittelijät voidaan linkittää ketjuun.
(2) ConcreteHandler (betoninkäsittelijä): Se on abstraktin käsittelijän alaluokka ja pystyy käsittelemään käyttäjien pyyntöjä.Abstraktissa prosessorissa määritetty abstraktin pyynnön käsittelymenetelmä on toteutettu konkreettisessa prosessoriluokassa, ja se voi käyttää ketjun seuraavaa objektia saavuttaakseenPyydä edelleenlähetystäVaikutus.
Chain of Responsibility -mallissa jokaisen kohteen viittaukset sen jälkeläisiin yhdistetään ketjuksi. Pyynnöt välitetään tästä ketjusta, kunnes mikä ketjun objekti lopulta käsittelee pyynnön. Tämä antaa järjestelmän dynaamisesti järjestää ketjun uudelleen ja määrittää vastuut asiakasta vaikuttamatta.

3. Tyypillinen koodi

Vastuuketjumallin ydin on abstraktin käsittelijäluokan suunnittelussa. Tyypillinen abstraktin käsittelijäluokan koodi on seuraava:

abstract class Handler {
    //维持对下家的引用
    protected Handler successor;
    
    public void setSuccessor(Handler successor) {
        this.successor = successor;    
    }
    
    public abstract void handleRequest(String request);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Yllä olevassa koodissa abstrakti prosessori määrittää viiteobjektin seuraavalle prosessorille pyynnön välittämiseksi seuraavalle prosessorille. Tämän objektin accessori voidaan asettaa suojatuksi, jota voidaan käyttää sen alaluokissa.

Betoninkäsittelijät ovat abstraktien käsittelijöiden alaluokkia, ja niillä on kaksi päätehtävää:

  • Pyyntöjen käsittelemiseksi eri prosessorit toteuttavat abstraktin pyynnön käsittelymenetelmän handleRequest() eri muodoissa;
  • Lähetä pyyntö edelleen, jos pyyntö ylittää nykyisen prosessorin käsittelyalueen, pyyntö voidaan välittää seuraavalle prosessorille.

Tyypillinen koodi tietylle käsittelijäluokalle on seuraava:

class ConcreteHandler extends Handler {
    public void handleRequest(String request) {
        if (请求满足条件) {
            //处理请求        
        } 
        else {
            this.successor.handleRequest(request);  //转发请求
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4. Tapaustutkimus 1: Ostotilausten hyväksymisjärjestelmä

Tietty ostotilausten hyväksymisjärjestelmä on hierarkkinen, eli sen hyväksyvät eri tasoiset esimiehet ostosumman mukaan.

Johtaja voi hyväksyä alle 50 000 juanin ostotilaukset, varapuheenjohtaja voi hyväksyä ostotilaukset [5 100 000 yuania], hallituksen puheenjohtaja voi hyväksyä ostotilaukset [10 500 000 yuania] ja 500 000 yuania ja edellä tarvitaan hallituksen kokous keskustelemaan ja päättämään.
Lisää kuvan kuvaus tähän

Koska kunkin viran hyväksyjillä on alaisia ​​(paitsi hallitus) ja heidän käyttäytymisensä on yhteistä, he kaikki liittyvät hyväksyntöjen välittämiseen seuraajilleen. Suunnittele siis hyväksyjäluokka abstraktiksi prosessoriksi:

//审批者类: 抽象处理者
abstract class Approver {
    protected Approver successor;  //定义后继对象
    protected String name;  //审批者姓名

    public Approver(String name) {
        this.name = name;
    }

    //设置后继者
    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    public abstract void processRequest(PurchaseRequest request);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Sitten jokaisen aseman, tietyn prosessorin, on toteutettava abstrakti prosessori:

//主任: 具体处理者
class Director extends Approver{
    public Director(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 50000) {
            System.out.println(
                    MessageFormat.format("主任 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);  //转发请求
        }
    }
}

//副董事长:具体处理类
class VicePresident extends Approver{
    public VicePresident(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 100000) {
            System.out.println(
                    MessageFormat.format("副董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);
        }
    }
}

//董事长类:具体处理者
class President extends Approver{
    public President(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 500000) {
            System.out.println(
                    MessageFormat.format("董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);
        }
    }
}

//董事会类:具体处理者
class Congress extends Approver{
    public Congress(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        System.out.println(
                MessageFormat.format("召开董事会 审批采购单:{0}, 金额:{1}元, 采购目的:{2}。",
                        request.getNumber(), request.getAmount(), request.getPurpose())
        );
    }
}
  • 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
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

Määritä toinen ostotilausluokka kohteeksi, joka on hyväksyttävä:

//采购单: 请求类
class PurchaseRequest {
    private double amount;  //采购金额
    private int number;  //采购单编号
    private String purpose;  //采购目的

    public PurchaseRequest(double amount, int number, String purpose) {
        this.amount = amount;
        this.number = number;
        this.purpose = purpose;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getPurpose() {
        return purpose;
    }

    public void setPurpose(String purpose) {
        this.purpose = purpose;
    }
}
  • 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
  • 34
  • 35
  • 36

Kirjoita asiakkaan testikoodi:

class Client {
    public static void main(String[] args) {
        Approver kangXi, yongZheng, qianLong, hanLinYuan;
        kangXi = new Director("康熙");
        yongZheng = new VicePresident("雍正");
        qianLong = new VicePresident("乾隆");
        hanLinYuan = new Congress("翰林院");

        //创建职责链
        kangXi.setSuccessor(yongZheng);
        yongZheng.setSuccessor(qianLong);
        qianLong.setSuccessor(hanLinYuan);

        //创建采购单
        PurchaseRequest pr1 = new PurchaseRequest(45000, 10001, "购买刀");
        kangXi.processRequest(pr1);

        PurchaseRequest pr2 = new PurchaseRequest(60000, 10002, "购买枪");
        kangXi.processRequest(pr2);

        PurchaseRequest pr3 = new PurchaseRequest(160000, 10003, "购买火炮");
        kangXi.processRequest(pr3);

        PurchaseRequest pr4 = new PurchaseRequest(800000, 10004, "购买军舰");
        kangXi.processRequest(pr4);
    }
}
  • 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

Käännä ja suorita ohjelma, tulos on seuraava:

主任 康熙 审批采购单:10,001, 金额:45,000元, 采购目的:购买刀。
副董事长 雍正 审批采购单:10,002, 金额:60,000元, 采购目的:购买枪。
董事长 乾隆 审批采购单:10,003, 金额:160,000元, 采购目的:购买火炮。
召开董事会 审批采购单:10,004, 金额:800,000元, 采购目的:购买军舰。
  • 1
  • 2
  • 3
  • 4

5. Tapausanalyysi 2: Luokkatehtävän käsittely

Suunnitellaan hieman yllä olevasta poikkeava tapaus, joka voi laajentaa ymmärrystämme vastuullisuusketjumallista ja skenaarioista, joissa sitä voidaan soveltaa.

  • Ensimmäinen ero: käsittelijän pyyntömenetelmällä on palautusarvo ja se voi palauttaa tietoja isännälle;
  • Toinen ero: "Ostotilauksen hyväksymisjärjestelmä" -tapaus käsittelee pyynnön ensin ja välittää sen jälkeen pyynnön, kun taas "Class Task Processing" -tapaus välittää pyynnön ensin ja käsittelee sitten pyynnön.

Koulu antaa luokalle joitain tehtäviä käsittelyyn. Näitä tyyppejä ovat yksi, kaksi, kolme, neljä jne. Luokanopettaja pystyy käsittelemään kolmen tyyppisiä tehtäviä yksi, kaksi, kolme ja monitori voi käsitellä kahta tyyppiä. yksi, kaksi tehtävät, oppimistoimikunta voi hoitaa yhden tämäntyyppisistä tehtävistä. Kun luokanopettaja saa tehtävän, hän luovuttaa sen ensin ryhmänjohtajalle, jos seuraava luokka ei pysty käsittelemään sitä, luokanopettaja saa sen ensin koulutoimikunnalle käsittelyä varten, jos seuraava luokka ei pysty käsittelemään sitä. Ryhmänjohtaja hoitaa tehtävän itse, jos se pystyy. Ja kun he eivät kestä sitä, he antavat palautetta ylöspäin.

Handler-luokka on suunniteltu alla abstraktiksi prosessoriksi:
Koska sinä ja alaiset eivät välttämättä pysty käsittelemään tiettyjä luokan tehtäviä, ja palaute on ylöspäin, prosessorin pyyntömenetelmällä on oltava looginen palautusarvo, jotta hän voi kertoa esimiehelle, pystyykö se käsittelemään tehtävän.

abstract class Handler {
    protected Handler successor;  //定义后继对象

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract boolean handleRequest(String taskName);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Alla luokanopettaja, ryhmänjohtaja ja opintotoimikunnan jäsen on vastaavasti suunniteltu erityisiksi käsittelijöiksi.
Oletuksena luokanopettaja antaa tehtävän ensin monitorille ja valvoja antaa tehtävän oletuksena opintotoimikunnan jäsenelle. Opintotoimikunnan jäsen voi käsitellä vain yhden tyyppisiä tehtäviä, ja jos se ei pysty käsittelemään sitä palauttaa väärän;
Jos ryhmänjohtajan saama palaute on väärä, hän hoitaa sen itse. Jos hän ei pysty käsittelemään sitä, hän vastaa väärin.
Jos rehtorin saama palaute on väärä, hän käsittelee sen itse. Jos hän ei pysty käsittelemään tehtäviä, hän vastaa väärin.

//班主任
class HeadTeacher extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled = successor.handleRequest(taskName);
        if (handled) {
            return true;
        }
        if (taskName.equals("one") || taskName.equals("two") || taskName.equals("three")) {
            System.out.println("班主任处理了该事务");
            return true;
        }

        return false;
    }
}

//班长
class Monitor extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled = successor.handleRequest(taskName);
        if (handled) {
            return true;
        }
        if (taskName.equals("one") || taskName.equals("two")) {
            System.out.println("班长处理了该事务");
            return true;
        }

        return false;
    }
}

//学习委员
class StudyCommissary extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled;
        if (successor == null) {  //注意学习委员可能没有下家,所以这里判一下是否为空
            handled = false;
        } else {
            handled = successor.handleRequest(taskName);
        }

        if (handled) {
            return true;
        }
        if (taskName.equals("one")) {
            System.out.println("学习委员处理了该事务");
            return true;
        }

        return false;
    }
}
  • 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
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

Kirjoita asiakkaan testikoodi:
Aseta jokaiselle viralle alitalot, mutta huomioi, että opintotoimikunnan jäsenille ei ole alitaloa.
Lisäksi, jos on tehtävä, jota rehtori ei voi käsitellä, tulosta lokirivi sen selittämiseksi.

public class SchoolClient {
    public static void main(String[] args) {
        Handler headTeacher, monitor, studyCommissary;
        headTeacher = new HeadTeacher();
        monitor = new Monitor();
        studyCommissary = new StudyCommissary();

        headTeacher.setSuccessor(monitor);
        monitor.setSuccessor(studyCommissary);
        studyCommissary.setSuccessor(null);  //没有下一职责人

        startRequest(headTeacher, "one");
        startRequest(headTeacher, "two");
        startRequest(headTeacher, "three");
        startRequest(headTeacher, "four");
    }

    private static void startRequest(Handler headTeacher, String taskName) {
        if (! headTeacher.handleRequest(taskName)) {
            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
  • 23

Käännä ja suorita ohjelma, tulos on seuraava:

学习委员处理了该事务
班长处理了该事务
班主任处理了该事务
该班级处理不了此类任务!
  • 1
  • 2
  • 3
  • 4

6. Sovellettavat skenaariot

Vastuuketjumallia voidaan harkita seuraavissa tilanteissa:

  • On olemassa useita objekteja, jotka voivat käsitellä samaa pyyntöä. Pyynnön käsittelevä objekti määritetään suorituksen aikana.Asiakkaan tarvitsee vain lähettää pyyntö ketjulle välittämättä siitä, kuka pyynnön käsittelee ja miten se käsitellään.
  • Lähetä pyyntö yhdelle useista objekteista määrittelemättä nimenomaisesti vastaanottajaa
  • Joukko objekteja voidaan määrittää dynaamisesti käsittelemään pyyntöjä.Asiakas voi luoda dynaamisesti vastuuketjun käsitelläkseen pyyntöjä ja voi myös muuttaa järjestystä ketjun prosessorien kesken.


Lähdekirjat:
"The Art of Design Patterns" - Liu Wei