在此组合上使用装饰器图案

时间:2012-09-29 17:52:19

标签: java decorator composition

我使用了合成模式,其中一个图形对象在组和矩形中扩展。一个组可以有多个矩形和一个paint()方法调用组,调用矩形中的绘制方法,每个矩形绘制自己。

现在我需要以某种方式使用装饰器图案,将矩形对齐到左侧或右侧。现在,如果我在组上使用装饰器,它只是对齐我猜的组坐标,但由于所有矩形都是自己绘制的,所以它对它们没有影响。

那么我可以在这里使用装饰模式的任何想法吗?

我的意思是我能想到使用它的唯一方法是在每个矩形和组中需要对齐的每个元素周围使用装饰器包装器。但是这不符合装饰者模式的政策吗?我的意思是该组在技术上不知道它的列表中有什么,对于它来说,元素只是GraphicObject实例,而矩形不知道它们是用装饰器缠绕的,它会在调用矩形的绘制之前修改它们的坐标( )方法。

如果这是唯一的方法,那么我该如何使用它来装饰这些物品?我必须在这里使用反射,还是有其他/更正确的方法来做到这一点?

一些代码:

if (mainGroup == null) {
        mainGroup = new Group();

        // first line
        currentBounds = convertToBoundsForLine(x, y, width, lineHeight, 1);
        //System.out.format("current bounds: x1=%d, y1=%d, x2=%d, y2=%d%n", currentBounds.getX(), currentBounds.getY(), currentBounds.getX2(), currentBounds.getY2());
        GraphicObject rect1 = new Rectangle(currentBounds);
        mainGroup.addChild(rect1);

        // other lines...

        // fifth line
        currentBounds = convertToBoundsForLine(x, y, width, lineHeight, 5);
        //System.out.format("current bounds: x1=%d, y1=%d, x2=%d, y2=%d%n", currentBounds.getX1(), currentBounds.getY1(), currentBounds.getX2(), currentBounds.getY2());
        GraphicObject rect5 = new Rectangle(currentBounds);

        // this is with the decorator wrapper around - should I use reflection here?
        GraphicObject blabla = new DecoratorLeft(rect5);
        mainGroup.addChild(blabla);

在第五行,你可以看到我将矩形放在装饰器中的位置,然后再添加到组中,所以我应该使用反射并在添加到组中的每个矩形上使用它们。或者有更好的方法吗?

或者我在这里使用装饰器的方式总体上是错误的吗?

提前致谢。

编辑:

以下是我在组中实现paint方法的方法:

public void draw(Graphics g, AlignStrategy align) {
    for (GraphicObject graphic: children) {
        graphic.draw(g, align);
    }
}

该小组只是调用儿童绘画方法。

问题是,装饰者在我的意见中应该是环球,根据你给出的建议似乎它会使它依赖?我的意思是该组基本上依赖于对齐?如果说我创建了一个可以旋转对象的装饰器,或者用y切换坐标x怎么办?我的意思是它不是关于左,右或中心的对齐,而是关于装饰器工作而不必过多地将代码调整到装饰器?

1 个答案:

答案 0 :(得分:1)

如果您完全控制GraphicObjectGroupRectangle等背后的代码(即,如果它是您的所有代码),那么我认为使用装饰器模式可能是艰难的路要走。使用类似于AWT的Layout方法的代码实现绘图对齐会更简单,其中父对象(在您的情况下可能是Group)被赋予一个负责的Layout个对象管理父母子女的安排。默认布局不会影响子项。 “左对齐”和“右对齐”布局将在绘制前一段时间的布局阶段对子项进行必要的位置更改。因此,Group将有一个默认的Layout对象,当您需要时,它可以被特定于对齐的版本替换。

如果您无法控制绘图对象背后的代码,那么我认为围绕Group的装饰器比装饰所有孩子更好。让Group级别的装饰器paint()方法管理子项调用时的对齐方式。这样,每个Group只有一个装饰器,而不是每个可绘制对象一个装饰器。这本质上就是布局代码,这些布局代码被装入一个装饰器类,但你总是可以打破布局代码并让装饰者调用它。

编辑:这是第二种情况的一个例子。为了简单起见,我只列出必要的部分,而不是详细信息。

public class GroupDecorator extends GraphicObject { //or extends whatever is best
    public GroupDecorator(Group wrappedGroup) { 
        myGroup = wrappedGroup;
    }

    public void paint() { //Assume this is your common paint() method
        //note I'm making up the names here, since I don't know them
        List groupsChildren = myGroup.getChildren(); 

        //The type of alignment is stored somewhere in this object
        //or is hard-coded. That's up to you. I'll assume it's stored somewhere...

        if (isAlignedLeft()) { 
          //align all the children to the left here
        } else if (isAlignedRight()){
          //align all the children to the right here
        } else { 
          //do nothing because no alignment is necessary
        }

        //call the wrapped Group's paint now. It will call its children's paint()s.
        myGroup.paint();

        //Unalign all the children here, if necessary, to put them back in 
        //their original state. How you implement that is up to you.
    }

}
// Somewhere in your main code...

    mainGroup = new Group();

    //Decorate mainGroup now...
    GroupDecorator decoratedGroup = new GroupDecorator(mainGroup);
    //Configure decoratedGroup to align however you want.

    // first line
    GraphicObject rect1 = new Rectangle();
    mainGroup.addChild(rect1);

    // other lines...

    // fifth line
    GraphicObject rect5 = new Rectangle();


    //Now use decoratedGroup now in place of mainGroup.