Composite

Um problema a resolver: editor de documentos

Composição recursiva

O Padrão Composite

Objetivo

Participantes

Consequências do uso de Composite

Considerações de implementação

Uso do padrão na API Java: O pacote java.awt.swing

Exemplo de código: um editor de documentos

editor4.gif (12197 bytes)

interface ElementoDeDocumentoIF {
    ElementoDeDocumentoIF getPai();
    /**
     * Retorna o font associado a este objeto.
     * Se não houver font, retorna o font do pai.
     * Se não houver pai, retorna null.
     */
    Font getFont();

    /**
     * Associa um font a este objeto.
     */
    void setFont(Font font);

    /**
     * Retorna o número de glyphs que este objeto contém.
     */
    int getNumGlyph();

    /**
     * Retorna o filho deste objeto na posição dada.
     */
    ElementoDeDocumentoIF getFilho(int índice)
            throws ÉFolhaException;

    /**
     * Faça o ElementoDeDocumentoIF dado um filho deste objeto.
     */
    void insereFilho(ElementoDeDocumentoIF filho)
            throws ÉFolhaException;

    /**
     * remove o ElementoDeDocumentoIF dado
     * da lista de filhos deste objeto.
     */
    void removeFilho(ElementoDeDocumentoIF filho)
            throws ÉFolhaException;
} // interface ElementoDeDocumentoIF

abstract Class ElementoDeDocumento
            implements ElementoDeDocumentoIF {
    // Este é o font associado ao objeto
    // Se for nulo, o font é herdado do pai
    private Font font;

    // o container deste objeto
    ElementoDeDocumentoIF pai;
    ...
    public ElementoDeDocumentoIF getPai() {
        return pai;
    }

    public Font getFont() {
        if(font != null) {
            return font;
        } else if(pai != null) {
            return pai.getFont();
        } else {
            return null;
        }
    } // getFont()

    public void setFont(Font font) {
        this.font = font;
    } // setFont()

    public abstract int getNumGlyph();

    public ElementoDeDocumentoIF getFilho(int índice)
            throws ÉFolhaException {
        throw new ÉFolhaException("Folha não tem filhos");
    }

    /**
     * Faça o ElementoDeDocumentoIF dado
     * um filho deste objeto.
     */
    public void insereFilho(ElementoDeDocumentoIF filho)
            throws ÉFolhaException {
        throw new ÉFolhaException("Folha não tem filhos");
    }

    /**
     * Remove o ElementoDeDocumentoIF dado
     * da lista de filhos deste objeto.
     */
    public void removeFilho(ElementoDeDocumentoIF filho) {
            throws ÉFolhaException {
        throw new ÉFolhaException("Folha não tem filhos");
    }
} // class ElementoDeDocumento

abstract class ElementoCompostoDeDocumento
            extends ElementoDeDocumento
            implements ElementoDeDocumentoIF {
    // Os filhos deste objeto
    private Collection filhos = new Vector();

    // Valor em cache de getNumGlyph
    private int cacheNumGlyph;
    // Validade de cacheNumGlyph
    private boolean cacheValida = false;

    /**
     * Retorna o filho deste objeto na posição dada.
     */
    public ElementoDeDocumentoIF getFilho(int índice) {
        return (ElementoDeDocumentoIF)filhos.get(índice);
    } // getFilho()

    /**
     * Faça o ElementoDeDocumentoIF dado
     * um filho deste objeto.
     */
    public synchronized void insereFilho(ElementoDeDocumentoIF filho) {
        synchronized(filho) {
            filhos.add(filho);
            filho.pai = this;
            avisoDeMudança();
        } // synchronized
    } // insereFilho()

    /**
     * Remove o ElementoDeDocumentoIF dado
     * da lista de filhos deste objeto.
     */
    public synchronized void removefilho(ElementoDeDocumentoIF filho) {
        synchronized(filho) {
            if(this == filho.pai) {
                filho.pai = null;
            }
            filhos.remove(filho);
            avisoDeMudança();
        } // synchronized
    } // removeFilho()
    ...
    /**
     * Uma chamada a esta função significa que um filho mudou,
     * o que invalida a cache de informação que o objeto mantém
     * sobre seus filhos.
     */
    public void avisoDeMudança() {
        cacheValida = false;
        if(pai != null) {
            pai.avisoDeMudança();
        }
    } // avisoDeMudança()

    /**
     * Retorna o número de glyphs que este objeto contém.
     */
    public int getNumGlyph() {
        if(cacheValida) {
            return cacheNumGlyph;
        }
        cacheNumGlyph = 0;
        for(int i = 0; i < filhos.size(); i++) {
            ElementoDeDocumentoIF filho;
            filho = (ElementoDeDocumentoIF)filhos.get(i);
            cacheNumGlyph += filho.getNumGlyph();
        } // for
        cacheValida = true;
        return cacheNumGlyph;
    } // getNumGlyph()
} // class ElementoCompostoDeDocumento

class Caractere extends ElementoDeDocumento
            implements ElementoDeDocumentoIF {
    ...
    /**
     * Retorna o número de glyphs que este objeto contém.
     */
    public int getNumGlyph() {
        return 1;
    } // getNumGlyph()
} // class Caractere

class Imagem extends ElementoDeDocumento
            implements ElementoDeDocumentoIF {
    ...
    /**
     * Retorna o número de glyphs que este objeto contém.
     */
    public int getNumGlyph() {
        return 1;
    } // getNumGlyph()
} // class Imagem

class Página extends ElementoCompostoDeDocumento {
            implements ElementoDeDocumentoIF {
    ...
} // Página

Perguntas finais para discussão

pat-5 programa anterior próxima