DESIGN PATTERN #3: COMPOSITE

COMPOSIÇÃO RECURSIVA

COMPOSITE


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

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

    /**
     * Retorna o font associado a este objeto.
     * Se não houver font, retorna o font do pai.
     * Se não houver pai, retorna null.
     */
    public Font getFont() {
        if( font != null ) {
            return font;
        } else if( pai != null ) {
            return pai.getFont();
        } else {
            return null;
        }
    } // getFont()

    /**
     * Associa um font a este objeto.
     */
    public void setFont( Font font) {
        this.font = font;
    } // setFont()

    /**
     * Retorna o número de caracteres que este objeto contém.
     */
    public abstract int getNumCarac();

    /**
     * Retona o filho deste objeto na posição dada.
     */
    public ElementoDeDocumento getFilho( int índice )
      throws ÉFolhaException {
        throw new ÉFolhaException( "Folha não tem filhos" );
    }

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

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

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

    // valor em cache de getNumCarac
    private int cacheNumCarac = -1; // -1 significa sem valor em cache

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

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

    /**
     * Remove o ElementoDeDocumento dado da lista de filhos deste objeto.
     */
    public synchronized void removeFilho( ElementoDeDocumento filho ) {
        synchronized (filho) {
            if( this == filho.pai ) {
                filho.pai = null;
            }
            filhos.removeElement( 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() {
        cacheNumCarac = -1;
        if( pai != null ) {
            pai.avisoDeMudança();
        }
    } // avisoDeMudança()

    /**
     * retorna o número de caracteres que este objeto contém.
     */
    public int getNumCarac() {
        if(cacheNumCarac >= 0) {
            return cacheNumCarac;
        }
        cacheNumCarac = 0;
        for( int i = 0; i < filhos.size(); i++ ) {
            ElementoDeDocumento filho;
            filho = (ElementoDeDocumento)filhos.elementAt( i );
            cacheNumCarac += filho.getNumCarac();
        } // for
        return cacheNumCarac;
    } // getNumCarac()
} // class ElementoCompostoDeDocumento

class Caractere extends ElementoDeDocumento {
...
    /**
     * retorna o número de caracteres que este objeto contém.
     */
    public int getNumCarac() {
        return 1;
    } // getNumCarac()
} // class Caractere

class Imagem extends ElementoDeDocumento {
...
    /**
     * retorna o número de caracteres que este objeto contém.
     */
    public int getNumCarac() {
        return 1; // uma imagem é considerada um "grande" caractere
    } // getNumCarac()
} // class Imagem

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