适配器模式与Liskov替换

Adapter Pattern vs Liskov Substitution

适配器设计模式用于将类(目标)的接口转换为客户机期望的另一个接口(适配器)。适配器允许不兼容的类一起工作,否则由于它们的接口不兼容而无法一起工作。

适配器模式可以通过两种方式实现:继承(适配器模式的类版本)和组合(适配器模式的对象版本)。

我的问题是关于使用继承实现的适配器模式的类版本。

以下是绘图编辑器的示例:

Figure 1:

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
interface Shape  
{  
        Rectangle BoundingBox();  

        Manipulator CreateManipulator();  
}  

class TextView  
{  
        public TextView() { }  

        public Point GetOrigin() { }  

        public int GetWidth() { }  

        public int GetHeight() { }  
}  
interface Shape
{
        Rectangle BoundingBox();

        Manipulator CreateManipulator();
}

class TextView
{
        public TextView() { }

        public Point GetOrigin() { }

        public int GetWidth() { }

        public int GetHeight() { }
}

我们希望重用textview类来实现textshape,但是接口是不同的,因此,textview和shape对象不能互换使用。

应该更改textView类以符合形状接口吗?也许不是。

textShape可以通过以下两种方式之一使textView接口适应形状的接口:

  • 通过继承形状的接口和textView的实现(适配器模式的类版本)
  • 通过在textShape对象内组合textView实例,并使用textView实例(适配器模式的对象版本)实现textShape的接口。
  • 类适配器

    Figure 2:

    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
    interface Shape  
    {  
        Rectangle BoundingBox();  

        Manipulator CreateManipulator();  
    }  

    class TextView  
    {  
        public TextView() { }  

        public Point GetOrigin() { }  

        public int GetWidth() { }  

        public int GetHeight() { }  
    }  

    class TextShape : TextView, Shape  
    {  
        public Rectangle BoundingBox()  
        {  
            Rectangle rectangle;  
            int x, y;  
            Point p = GetOrigin();  
            x = GetWidth();  
            y = GetHeight();  

            //...  

            return rectangle;  
        }  

        #region Shape Members  

        public Rectangle Shape.BoundingBox()  
        {  
            return new TextBoundingBox();  
        }  

        public Manipulator Shape.CreateManipulator()  
        {  
            return new TextManipulator();  
        }  

        #endregion  
    }

    现在回答问题:—)。textShape继承自shape,尤其是textView,是否是有效的"is a"关系?如果不是,这是否违反了里斯科夫的替代原则?


    它不会违反Liskov替换原则,除非子类中有一些东西使它的行为对超类没有意义(违反了超类的契约)。当然,这是不完整的代码,但我看不到任何迹象。

    它可能违反了单一责任原则,但我不确定在适配器实现中这是一个巨大的问题。

    我一般更喜欢代表的方式。