Condivisione della tecnologia

Modello di progettazione Modello di catena di responsabilità

2024-07-12

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

1. Concetto

Modello di catena di responsabilità: evitare di accoppiare il mittente e il destinatario della richiesta, dare a più oggetti l'opportunità di ricevere la richiesta, connettere questi oggetti in una catena e passare la richiesta lungo questa catena finché non c'è un oggetto per gestirla.Il modello della catena di responsabilità è amodello di comportamento dell'oggetto

2. Struttura

Il nucleo della struttura del modello della catena di responsabilità è l'introduzione di aprocessore astratto
Inserisci qui la descrizione dell'immagine

Come si può vedere dalla figura, il diagramma della struttura del modello di catena di responsabilità contiene i seguenti due ruoli:
(1) Gestore (gestore astratto): definisce un'interfaccia per l'elaborazione delle richieste ed è generalmente progettato come una classe astratta. Poiché diversi gestori concreti gestiscono le richieste in modo diverso, in essi sono definiti metodi di gestione delle richieste astratte.Poiché il subordinato di ciascun processore è ancora un processore, un oggetto di tipo processore astratto (successore nel diagramma della struttura) viene definito nel processore astratto come suo subordinato.Citazione . Attraverso questo riferimento, gli handler possono essere collegati in una catena.
(2) ConcreteHandler (gestore concreto): è una sottoclasse del gestore astratto e può gestire le richieste dell'utente.Il metodo di elaborazione della richiesta astratta definito nel processore astratto è implementato nella classe del processore concreto e può accedere all'oggetto successivo nella catena per ottenereRichiesta di inoltroEffetto.
Nel modello Catena di responsabilità, i riferimenti di ciascun oggetto ai suoi discendenti sono collegati per formare una catena. Le richieste vengono passate su questa catena fino a quando l'oggetto nella catena gestisce infine la richiesta. Ciò consente al sistema di riorganizzare dinamicamente la catena e assegnare le responsabilità senza influenzare il client.

3.Codice tipico

Il nucleo del modello della catena di responsabilità risiede nella progettazione della classe del gestore astratto. Il codice tipico della classe del gestore astratto è il seguente:

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

Nel codice precedente, il processore astratto definisce un oggetto di riferimento al processore successivo per inoltrare la richiesta al processore successivo. La funzione di accesso di questo oggetto può essere impostata su protected, che può essere utilizzata nelle sue sottoclassi.

I gestori concreti sono sottoclassi dei gestori astratti e hanno due funzioni principali:

  • Per elaborare le richieste, diversi processori specifici implementano il metodo di elaborazione delle richieste astratte handleRequest() in diverse forme;
  • Inoltra la richiesta Se la richiesta supera l'intervallo di elaborazione del processore corrente, la richiesta può essere inoltrata al processore successivo.

Il codice tipico per una classe di gestore specifica è il seguente:

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. Caso di studio 1: Sistema di approvazione degli ordini di acquisto

Un certo sistema di approvazione degli ordini di acquisto è gerarchico, ovvero è approvato dai supervisori a diversi livelli in base all'importo di acquisto.

Il direttore può approvare ordini di acquisto inferiori a 50.000 yuan, il vicepresidente può approvare ordini di acquisto di [5.100.000 yuan], il presidente del consiglio di amministrazione può approvare ordini di acquisto di [10.500.000 yuan] e ordini di acquisto di 500.000 yuan e soprattutto è necessaria una riunione del consiglio per discutere e decidere.
Inserisci qui la descrizione dell'immagine

Poiché gli approvatori di ciascuna posizione hanno dei subordinati (ad eccezione del consiglio di amministrazione) e i loro comportamenti sono comuni, tutti implicano l'inoltro delle approvazioni ai loro successori. Quindi progetta una classe approvatore come un processore astratto:

//审批者类: 抽象处理者
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

Quindi ogni posizione, come processore specifico, deve implementare un processore astratto:

//主任: 具体处理者
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

Definire un'altra categoria dell'ordine d'acquisto come obiettivo da approvare:

//采购单: 请求类
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

Scrivi il codice di test del client:

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

Compilare ed eseguire il programma, l'output è il seguente:

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

5. Analisi del caso 2: elaborazione dei compiti di classe

Progettiamo un caso leggermente diverso da quello sopra, che possa ampliare la nostra comprensione del modello della catena di responsabilità e degli scenari in cui può essere applicato.

  • La prima differenza: il metodo di richiesta dell'handler ha un valore di ritorno e può restituire informazioni all'host;
  • La seconda differenza: il caso "Sistema di approvazione degli ordini di acquisto" elabora prima la richiesta e poi la inoltra, mentre il caso "Elaborazione attività di classe" inoltra prima la richiesta e poi la elabora.

La scuola assegnerà alcuni compiti alla classe per l'elaborazione, questi tipi includono uno, due, tre, quattro, ecc. L'insegnante della classe può gestire i tre tipi di compiti uno, due, tre e il monitor può gestire i due tipi di. uno, due. Compiti, il comitato di apprendimento può gestire uno di questo tipo di compiti. Quando l'insegnante di classe riceve un compito, lo consegnerà prima al caposquadra per l'elaborazione. Se la classe successiva non può gestirlo, l'insegnante di classe lo gestirà lui stesso. Quando l'insegnante di classe riceve il compito, lo consegnerà per primo al comitato scolastico per l'elaborazione. Se la classe successiva non è in grado di gestirlo, lo farà l'insegnante di classe. Il caposquadra se ne occuperà lui stesso quando il comitato scolastico riceve un compito, se può lo gestirà lui stesso; E quando non riescono a gestirlo, daranno un feedback verso l'alto.

La classe Handler è progettata di seguito come un processore astratto:
Poiché tu e i tuoi subordinati potreste non essere necessariamente in grado di gestire determinati compiti di classe e c'è un feedback positivo, il metodo di richiesta del processore deve avere un valore di ritorno booleano per indicare al superiore se è in grado di gestire l'attività.

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

Di seguito, l'insegnante di classe, il caposquadra e il membro del comitato di studio sono rispettivamente designati come gestori specifici.
Per impostazione predefinita, l'insegnante della classe assegnerà prima il compito al monitor e il monitor affiderà il compito al membro del comitato di studio per impostazione predefinita. Il membro del comitato di studio può gestire solo un tipo di compiti e, se non può, gestirlo restituirà false;
Se il feedback ricevuto dal caposquadra è falso, lo gestirà da solo. Può gestire solo uno o due tipi di compiti. Se non riesce a gestirlo, restituirà falso.
Se il feedback ricevuto dal dirigente scolastico è falso, lo gestirà lui stesso. Può gestire solo i compiti di uno, due e tre tipi. Se non è in grado di gestire i compiti, restituirà falso.

//班主任
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

Scrivi il codice di test del client:
Crea delle sottosedi per ciascuna posizione, ma tieni presente che non esiste una sottosede per i membri del comitato di studio.
Inoltre, se c'è un compito che il preside non può gestire, stampa una riga di registro per spiegarlo.

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

Compilare ed eseguire il programma, l'output è il seguente:

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

6. Scenari applicabili

Il modello Catena di responsabilità può essere considerato nelle seguenti situazioni:

  • Più oggetti possono gestire la stessa richiesta. L'oggetto specifico che gestisce la richiesta verrà determinato in fase di esecuzione.Il cliente deve solo inviare la richiesta alla catena senza preoccuparsi di chi la richiesta viene elaborata e di come viene elaborata.
  • Invia una richiesta a uno tra più oggetti senza specificare esplicitamente il destinatario
  • È possibile specificare dinamicamente un insieme di oggetti per gestire le richieste.Il cliente può creare dinamicamente una catena di responsabilità per gestire le richieste e può anche modificare l'ordine tra i processori nella catena.


Libri di riferimento:
"L'arte dei modelli di progettazione" - Liu Wei