1

I'm studying Design Patterns book and I was trying to understand the Composition principle in Lexi's Compositor-Composition as explained in figure 2.5, as well as how to implement it.

figure 2.5 - Compositor-Composer

Where the expected behavior is showed in figure 2.6: figure 2.6 - expected output

After some research, I couldn't find anything related to the same use case.

The only way I was able to do it, was trying to implemement myself, but I have no idea of what mistakes I did and would like to know if this approach is ok.


type Point = {
  // Unimplemented
}

abstract class Glyph {
    public readonly name: string
    constructor(
        name: string
    ) {
        this.name = name
    }
    abstract parent(): Glyph
    abstract child(index: number): Glyph
    abstract intersects(point: Point): boolean
    abstract remove(glyph: Glyph): void
    abstract insert(glyph: Glyph, index: number): void
}

class UnimplemenmtedGlyph extends Glyph {
    parent(): Glyph {
        throw('Not implemented')
    }
    child(index: number): Glyph {
        throw('Not implemented')
    }
    intersects(point: Point): boolean {
        throw('Not implemented')
    }
    remove(glyph: Glyph): void {
        throw('Not implemented')
    }
    insert(glyph: Glyph, index: number): void {
        throw('Not implemented')
    }
}


type GlyphChild = {
    index: number
    glyph: Glyph
}

class Composition extends Glyph {
    public childs: GlyphChild[] = []
    protected compositor?: Compositor
    insert(glyph: Glyph, index: number): void {
        const glyphChild = {
            index: index,
            glyph: glyph
        }
        this.childs.push(glyphChild)
    }
    remove(glyph: Glyph) {
        throw('Not implemented')
    }
    parent(): Glyph {
        throw('Not Implemented')
    }
    child(index: number): Glyph {
        return this.childs[index].glyph
    }
    intersects(point: Point): boolean {
        throw('Not implemented')
    }
    setComposition(compositor: Compositor) : void {
        this.compositor = compositor
    }
    onChangeEvent(): void {
        console.log('called onChangeEvent')
        console.log(this.compositor)
        this.compositor?.compose()
    }
}

abstract class Compositor {
    abstract compose(): void 
    abstract setComposition(composition: Composition): void
}

class SimpleCompositor extends Compositor {
    private composition?: Composition
    compose(): void {
        if(this.composition){
            console.log(`composing simple format with received composition ${this.composition.name}`)
            console.log(`containing ${this.composition.childs.length} childs:`)
            for (const childitem of this.composition.childs) {
                console.log(childitem.glyph.name)
            }
        }
    }
    setComposition(composition: Composition): void {
        this.composition = composition
    }
}



const test = () => {
    console.log('entered test')
    
    const rowComposite = new Composition('rowComposite', )
    const smallG = new UnimplemenmtedGlyph('smallG')
    const bigG = new UnimplemenmtedGlyph('bigG')
    const space = new UnimplemenmtedGlyph('space')
    const image = new  UnimplemenmtedGlyph('image')

    rowComposite.insert(smallG, 1)
    rowComposite.insert(bigG, 2)
    rowComposite.insert(space, 3)
    rowComposite.insert(image, 4)

    const simpleCompositor = new SimpleCompositor()
    simpleCompositor.setComposition(rowComposite)
    rowComposite.setComposition(simpleCompositor) // as explained in the end of the section

    // something changed!!!!
    rowComposite.onChangeEvent() // calls simpleCompositor.compose()


}

test()

// Output:
//
// composing simple format with received composition rowComposite
// containing 4 childs
// smallG
// bigG
// space
// image

Even that I didn't take care of column, would this example be an acceptable way to implement Compositor-Composition?

4
  • "Composition" simply means that an object holds a reference to another object, and that's all it means. It embodies a HAS A relationship; a car HAS A steering wheel, for example. Compare with Inheritance, which embodies an IS A relationship. A car IS A vehicle. See also en.wikipedia.org/wiki/Composition_over_inheritance Jul 17 at 21:41
  • OK, well it took a few minutes for me to figure out what this was all about. So Lexi is apparently a document editor, and the diagram you pasted here appears to be from a lecture PowerPoint that you found here. Given that this strategy is explained in great detail here, I would suggest a few things. Jul 17 at 21:55
  • 1
    First, make your question more explicit by explaining specifically where you are stuck. Second, explain what you mean by "acceptable," in a way that doesn't simply restate the term "acceptable" in a different way. State your criteria for acceptability, in other words. Finally, since this appears to be homework, you can ask your teacher for assistance, who can provide you with insight from the specific perspective of the course they are teaching. Jul 17 at 21:57
  • Hello @RobertHarvey. As explained, is not about powerpoint, is about the book everybody knows "Design Patterns". (linked in the question's first sentence). Secondly, is not a homework, I am reading the book and had the doubts about how to implement. Acceptable I mean: acceptable to implement the proposed UML in books figure 2.5. Jul 17 at 23:17
3

The idea behind Lexi's Composition/Compositor combination is that an empty document starts with a Composition without children and a concrete Compositor that determines how the formatting of the document will look like.

The Compositor is, for example, responsible for determining where to put a line break so that the text does not go beyond the width of the screen or the page.

Once you have a Composition object, the application inserts Glyph elements into it corresponding to the visual items (G, g, , the image) that the user inserts into the document. The Compositor adds the Row and Column elements into the object tree as needed to get the correct look of the document, with for example a line break after the car image.

The Composition/Compositor is not a design pattern by itself, but it is a showcase example of two design patterns (the Composite pattern and the Strategy pattern) being used in an actual application.

3
  • thanks for that Bart. that said, the test method makes sense then? Jul 19 at 19:06
  • The test method does not make sense to me for several reasons: 1) It is unclear what the scope of the test is (does it test only SimpleCompositor or the combination Composition/Compositor) 2) The test does not contain any validation that the results are as expected. Jul 20 at 9:29
  • thanks gonna consider that Jul 20 at 20:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.