就他吧-9ta8为您提供:身份证查询、15位转16位身份证,手机号码归属地查询,IP地址查询服务,城市天气预报查询,列车时刻表简易快速查询等等查询服务,就他吧欢迎您的光临!!
随笔- 96  评论- 750  文章- 16 

用实例解说Dot Net设计模式——装饰模式

1.装饰模式的概述
2.1 什么是装饰模式
        在不改变对象的前提下,动态增加其功能,即我们不希望改变原有的类,或采用创建子类的方法增加功能,这种情况下需要采用装饰模式。

2.2结构
        修饰一个对象后,其接口不应该发生变化;否则这个对象不能被原有调用者使用,修饰失去了意义。由此引出了装饰模式结构最重要的一点,即装饰者和被装饰者具有相同的接口。换句话说,动态增加的功能不应该破坏已有的接口。
装饰模式的结构如下图所示。
                                            
                                                           装饰模式的结构图
(1)Component:定义一个对象接口,可以动态添加这些对象的功能。
(2)ConcreteComponent:定义一个对象,可以为其添加一些功能。
(3)Decorator:维持一个对Component对象的引用,并定义与Component接口一致的接口。
(4)ConcreteDecorator:为组件添加功能。

2.3 什么情况下是用装饰模式
       以下情况使用装饰模式。
   (1)在不影响其他对象的情况下,以动态且透明的方式添加单个对象的功能。
   (2)处理那些可以撤销的功能。
   (3)不能采用生成子类的方法扩充时。

2.4实现时的注意事项
        实现时的注意事项如下。
(1)接口的一致性:装饰模式中被装饰对象对用户而言是透明的,用户仍然可以直接访问被装饰对象,并且装饰后的对象在访问时不就变化。如果接口不一致,尽管也达到了为对象增加功能的目的,但并不是装饰模式。
(2)保持被装饰类的简单性:被装饰类需要功能单一,这样才能使结构更灵活。比如显示最新新闻的组件,由标题和主体组成。

其中的核心组件应该显示若干条最近的新闻,我们可以用一个组件显示新闻,加一个显示标题和样式。解决问题的方法是核心组件保持最小的功能,显示新闻标题,每个装饰器也只完成一项功能。我们可以增加一个显示more按钮的装饰器,使得装饰器可以通过组合完成更多的功能。

 2.5效果
使用装饰模式的优点是比静态继承灵活,并可避免在层次结构高层的类有太多的特征;缺点是产生许多小对象。

2.用一个实例来解说装饰模式
        做BS的朋友们都会遇到网页组件装饰器的实现问题,我们希望为用户自定义组件创建可以通用的装饰器。为此需要定义一个装饰器的基类,代码如下:

 1Public Class Decorator
 2    Inherits System.Web.UI.UserControl
 3    Private _Control As String
 4    Private _ContentPane As Web.UI.Control
 5    Public Property Control() As String
 6        Get
 7            Return _Control
 8        End Get
 9        Set(ByVal Value As String)
10            _Control = Value
11        End Set
12    End Property
13    Public Property ContentPane() As Web.UI.Control
14        Get
15            Return _ContentPane
16        End Get
17        Set(ByVal Value As Web.UI.Control)
18            _ContentPane = Value
19        End Set
20    End Property
21
22    Public Sub AddControl(ByVal c As UserControl)
23        _ContentPane.Controls.Add(c)
24    End Sub
25End Class
26
首先需要注意Decorator必须与被装饰者具有相同的接口,因此它是UserControl的子类。在Decorator中定义了装载被装饰者的方法AddControl,和装载被装饰者的容器ContentPane。
在具体的装饰者中,后台的可执行代码很少,主要定义装饰界面,在SubDecorator中定义了带有图形边框的界面:
 1Public Class SubDecorator
 2    Inherits Decorator
 3    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
 4
 5    End Sub
 6    Protected WithEvents cp As System.Web.UI.HtmlControls.HtmlTableCell
 7    Private designerPlaceholderDeclaration As System.Object
 8    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
 9        InitializeComponent()
10        Me.ContentPane = Me.cp
11    End Sub
12    Public Sub Page_load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
13        If Me.Control <> "" Then
14            Dim c As UserControl = LoadControl(Me.Control)
15            Me.AddControl(c)
16        End If
17    End Sub
18End Class

在另一个装饰器中如下定义了可以使被装饰者滚动显示的SubDecoratorB.
Public Class SubDecoratorB
    Inherits Decorator

    Protected WithEvents Top As System.Web.UI.WebControls.Literal
    Protected WithEvents Buttom As System.Web.UI.WebControls.Literal
    Protected WithEvents cp As System.Web.UI.WebControls.Panel

#Region 
" Web 窗体设计器生成的代码 "

    
'该调用是 Web 窗体设计器所必需的。
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    
'注意: 以下占位符声明是 Web 窗体设计器所必需的。
    '不要删除或移动它。
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        
'CODEGEN: 此方法调用是 Web 窗体设计器所必需的
        '不要使用代码编辑器修改它。
        InitializeComponent()
        Me.ContentPane 
= Me.cp
    End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        
'在此处放置初始化页的用户代码
        If Me.Control <> "" Then
            Dim c As UserControl 
= LoadControl(Me.Control)
            Me.AddControl(c)
            Me.Top.Text 
= ShowRollBegin("100")
            Me.Buttom.Text 
= ShowRollEnd()

        End If
    End Sub

    Private Function ShowRollBegin(ByVal strDisplayHeight) As String
        Return 
"<marquee style='bottom:0px;height:'" & strDisplayHeight & "px;top:0px' id='News' scrollamount=1 scrolldelay=10 behavior='scroll' direction='up' onmouseover='this.stop()' onmouseout='this.start()'>"
    End Function

    Private Function ShowRollEnd() As String
        Return 
"</marquee>"
    End Function
End Class

我们定义一个很简单的HelloWorld用户组件用来测试,这个组件没有后台代码,仅仅有一个Lable组件:
<asp:Label Runat="server" id="Label2">HelloWorld</asp:Label>
我们用上面的两个装饰器装饰这个组件:
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        
'在此处放置初始化页的用户代码
        Dim c As Decorator = LoadControl("SubDecorator.ascx")
        Dim d As Decorator 
= LoadControl("SubDecoratorB.ascx")
        d.Control 
= "HelloWorld.ascx"
        Me.Controls.Add(c)
        c.AddControl(d)
    End Sub


感兴趣的朋友可以做一个例子看看效果,别看上面一大堆代码,其实仔细观察不难看出实际上是几个UserControl的相互套用来实现装饰模式的。

3.我们在看一个.NET中的装饰模式实例
        BufferedStream是经典的装饰模式,GOF在《设计模式》中已有介绍。并且在多种语言平台上都有相同的实现,在.NET中也不例外。
        BufferedStream的作用是为另一流上的读写操作添加一个缓冲层,缓冲区是内存中的字节块,用于缓存数据。从而减少对操作系统的调用次数,缓冲区可提高读取和写入性能。
        Stream是流的基类,抽象了对流的操作。根据流的来源和性质的不同,Stream的子类有System.Data.OracleClient.OracleBFile,System.Data.OracleClient.OracleLob,System.
IO.BufferedStream,System.IO.FileStream,System.IO.MemoryStream,System.Net.Sockets.NetworkStream和System.Security.Cryptography.CryptoStream。
        如FileStream可以打开一个文件并以流的方式读写文件,MemoryStream读写内存中的数据流,而NetworkStream则操作Scokets连接的TCP流。其中,BufferedStream非常特殊,它不直接创建英雄模范种类型的流,而是在已有流上执行附加的操作。由于它继承了Stream,因此使用上与一般的流没有不同,只是为现有的流增加了缓冲功能。
        通过采用装饰模式,BufferedStream使Stream具有了缓存功能而不用改动接口。这就是装饰模式所要达到的效果。这里就不再举具体的例子了,其实很简单,朋友可以自己试着写一下.

 


 

posted on 2005-12-24 13:45  振河  阅读(...)  评论(...编辑  收藏

  就他吧-9ta8伴您开心每一天