老生常谈:外观模式

本文深入讲解外观模式的应用,通过投票系统的实例,展示了如何利用外观模式简化客户端代码,降低子系统间的耦合度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    名词解释:外观:

       ①事物的外在形象:他外观宽绰,其实已经负债累累|这栋旧宅外观尚可,而内部已被白蚁蛀空。  

       ②外貌:外观整洁|既要注重外观,更要注重内在的修养。

    外观模式:外观模式(Facade pattern)涉及到子系统的一些类。所谓子系统,是为提供一系列相关的特征(功能)而紧密关联的一组类。外观模式其实说的通俗一点就是起到化零为整的作用。

 

    下图为没有应用外观模式时的类图:可以看出客户端代码直接与子系统类的耦合度太强。

 

    

    下面是应用了外观模式的类图:可以看出客户端只与外观类耦合,成功的与杂乱而多的子系统解耦。组装的任务就交给外观类去完成了。

 

 

     例如:一部F1赛车,它在比赛结束后都会被肢解为大大小小的零件,在下次开始比赛时又会把这些零件一一组装起来变成一部看起来十分亮眼的赛车。如果你面对一堆的零件,你要想把它开动,要如何是好呢?当然你会想到先把它组装起来。你之所以第一时间想到把它先组装起来,是因为你知道只要它变成一辆赛车(而不是一堆零件),然后一按启动按钮,赛车就会启动。原本是一堆看起来零乱的东西,为什么经过组装后操作就会变得如此简单呢,只要按一下,车就会全身动起来,而不是一个地方动呢?这里汽车设计师们就应用了外观模式。原来赛车的启动程序是非常复杂的,本人引用一个百度知道的内容:

    启动机通电后,磁力线圈,产生吸力,吸住拨叉,通过杠杆原理,把单向齿轮推出去,与发动机后部的飞轮捏合,与此同时,通电后的启动机转子会旋转,而单向齿轮通过花键随转子转动,带动发动机转动,从而启动!

    可见汽车的启动在内部是发生很多事情的,并不是一件事就解决问题了。上面的解释中:磁力线圈,产生吸力,吸住拨叉,通过杠杆原理,把单向齿轮推出去,这些内部的操作过程对于一般司机来说意义并不大,司机也不想去了解它的工作原理,要不然司机不都是汽车工程师了。司机的责任只是驾驶它,并不用去了解它的本质。既然不用了解工作原理,那汽车设计师就必须让司机非常方便的学会开车,例如让司机按一个开关汽车就会启动,实际生活中,他们也就是这样做的。

    上面生活中的例子在程序设计中也是一样的,往往在客户端程序中的一个功能应用会涉及到子系统中很多组件,如果客户端程序调用中出现大量的子系统代码,那么会有以下几个弊端:

    1:客户端代码过大,不容易维护。

    2:客户端代码依赖子系统,子系统一旦有变化,客户端程序就要变,这违背了开闭原则的编程规则。

   下面以一个网上投票的例子来说明一下。投票的流程如下:

      第一:检测是否是登录用户,如果未登录则提示登录。

      第二:登录用户余额是否充足,不足则提示。

      第三:防止用户刷票。

      第四:用户投票,并扣除用户费用。

    1:客户端页面代码:由于本文主旨在外观模式,为此在登录,扣费的地方就简单化了,登录用一个输入框完成,用户的帐户信息都是事先存储好的。只是为了说明问题而已。

ContractedBlock.gifExpandedBlockStart.gifCode
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        
<br />
        
<asp:Button ID="Button2" runat="server" onclick="Button2_Click" Text="投票" />
        
<br />
        
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        
<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="登录" />

 

   cs:  

ContractedBlock.gifExpandedBlockStart.gifCode
ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
        
/// 用户登录
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected void Button1_Click(object sender, EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif        
{
            Session[
"user"= this.TextBox1.Text.Trim();
            
this.JSMessageboxJM("登录成功"this.Request.Url.ToString());

        }

ExpandedBlockStart.gifContractedBlock.gif        
/**//// <summary>
        
/// 弹出提示框并跳转
        
/// </summary>
        
/// <param name="strmessage">要显示的消息字符串</param>
        
/// 日期:2007-09-24
        
/// 作者: 
        
/// 最后修改日期:
        
/// 最后修改人:minjiang

        public void JSMessageboxJM(string strmessage, string strlocationpage)
ExpandedBlockStart.gifContractedBlock.gif        
{
            
string strmes;
            
if (strlocationpage.Trim() != "")
                strmes 
= "<script language='javascript'>alert('" + strmessage + "');window.location.href='" + strlocationpage + "';</script>";
            
else
                strmes 
= "<script language='javascript'>alert('" + strmessage + "');</script>";
            Page.RegisterStartupScript(
"", strmes);
        }

ExpandedBlockStart.gifContractedBlock.gif        
/**//// <summary>
        
/// 用户投票事件
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected void Button2_Click(object sender, EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif        
{
            
//注册用户ID
            string sID = "";
            
//判断是否是注册用户.
            if (Session["user"== null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
this.Label1.Text = "只有注册用户才能投票!";
                
return;

            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sID 
= Session["user"].ToString();

            }

            
//判断注册用户的余额中否充足
            userBalance _userBalance = userBalance.getInstance();
            
//取得登录用户的余额
            int iBalance = _userBalance.getUserBalance(sID);
            
if (iBalance < 1)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
this.Label1.Text = "您的余额不足,请先充值!";

            }

            
//防止用户刷票,5分钟类只能投一次
            
//此处为了简化代码就不写程序了,默认为第一次投票.
            bool enableVote = true;
            
//用户投票操作
            bool isVote = _userBalance.updateUserBalance(sID);
            
if (isVote)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
this.Label1.Text = "投票成功,您的余额为:" + _userBalance.getUserBalance(sID).ToString();

            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
this.Label1.Text = "投票失败,请重试!";

            }


        }

 

    2:子系统代码:客户端代码并不是特别的多,但总觉的看起来不太清晰,有点把业务逻辑层的功能占用的嫌疑。这里就是一个投票功能,为何要在客户端代码中加入如此多的业务处理呢?(投票规则判断),如果能写成一个方法就好了。

ContractedBlock.gifExpandedBlockStart.gifCode
public class  userBalance
ExpandedBlockStart.gifContractedBlock.gif    
{
        
private IDictionary<stringint> _dictionary;
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
        
/// 存储用户的余额信息列表
        
/// </summary>
        
/// <returns>余额信息列表</returns>

        public IDictionary<string,int >  getUserBalanceList()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            _dictionary
=new Dictionary<string,int>() ;
            
for(int i=1;i<100;i ++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                _dictionary .Add (i.ToString (),i);
            
            }

            
return _dictionary;

        
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
        
/// 根据登录用户ID取得用户余额
        
/// </summary>
        
/// <param name="userID">用户ID</param>
        
/// <returns>对应用户余额</returns>

        public int getUserBalance(string userID)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
if (_dictionary == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                _dictionary 
= this.getUserBalanceList();
            }

            
if (_dictionary.ContainsKey(userID ) )
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
return int.Parse (_dictionary[userID].ToString());

            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
return 0;
            
            }


 
        
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
        
/// 用户投票,扣费.
        
/// </summary>
        
/// <param name="userID">登录用户ID</param>
        
/// <returns>投票是否成功</returns>

        public bool updateUserBalance(string userID)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
if (_dictionary == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    _dictionary 
= this.getUserBalanceList();
                }

                
//投票扣一元
                _dictionary[userID] = int.Parse (_dictionary[userID].ToString()) - 1;
                
//操作成功
                return true;
            }

            
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
//操作失败
                return false;
            
            }

 
        
        }

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
       
/// 定义自身的静态变量实例,默认值为null
       
/// instance被申请成readonly,也是为了避免给实例重新赋值.
       
/// </summary>

       private static userBalance instance = null;
ExpandedSubBlockStart.gifContractedSubBlock.gif       
/**//**//**//// <summary>
       
/// 创建一个object对象,同样它也是静态只读,用来实现锁定功能
       
/// </summary>

       private static readonly object olock = new object();
ExpandedSubBlockStart.gifContractedSubBlock.gif       
/**//**//**//// <summary>
       
/// 私有构造函数
       
/// 之所有不是public类型的,因为是为了避免发生new Singleton()产生更多的实例
       
/// </summary>

       private userBalance()
ExpandedSubBlockStart.gifContractedSubBlock.gif       
{
           _dictionary 
= this.getUserBalanceList();
       
       }

ExpandedSubBlockStart.gifContractedSubBlock.gif       
/**//**//**//// <summary>
       
/// 生成实例方法
       
/// </summary>
       
/// <returns></returns>

       public static userBalance getInstance()
ExpandedSubBlockStart.gifContractedSubBlock.gif       
{
           
if (instance == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif           
{
               
//取得实例的时候先锁定对象,然后判定是否存在
               lock (olock)
ExpandedSubBlockStart.gifContractedSubBlock.gif               
{
                   
//如果实例没有被初始化则实例化变量
                   if (instance == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                   
{
                       instance 
= new userBalance();

                   }


               }


           }

           
return instance;

       }



    }

 

    3:改进代码:子系统代码不变 。客户端投票代码:

    4:新增外观类:

ContractedBlock.gifExpandedBlockStart.gifCode
public class facadeVote
ExpandedBlockStart.gifContractedBlock.gif    
{
        
public string userVote()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
//投票处理结果
            string sResult = "";
            
//注册用户ID
            string sID = "";
            
//判断是否是注册用户.
            if (HttpContext .Current.Session["user"== null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sResult  
= "只有登录用户才能投票,请先登录!";
                
return sResult ;

            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sID 
= HttpContext.Current.Session["user"].ToString();

            }

            
//判断注册用户的余额中否充足
            userBalance _userBalance = userBalance.getInstance();
            
//取得登录用户的余额
            int iBalance = _userBalance.getUserBalance(sID);
            
if (iBalance < 1)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sResult  
= "您的余额不足,请先充值!";
                
return sResult;

            }

            
//防止用户刷票,5分钟类只能投一次
            
//此处为了简化代码就不写程序了,默认为第一次投票.
            bool enableVote = true;
            
//用户投票操作
            bool isVote = _userBalance.updateUserBalance(sID);
            
if (isVote)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sResult  
= "投票成功,您的余额为:" + _userBalance.getUserBalance(sID).ToString();
                
return sResult;

            }

            
else
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                sResult  
= "投票失败,请重试!";
                
return sResult;

            }

            
return sResult ;
        
        }

    }

 

    5:客户端调用代码如下:可见看起来简化多了。通过新增加的外观类,成功的把复杂的业务判断分散到外观类中,客户端只与外观类有直接关系,从而使得客户端与子系统之间解耦,外观模式在应用的时候,同时 允许客户端直接访问子系统,这样就和没有用外观模式时一样,这种情况大多应用在比较老的系统中,老系统由于升级,应用了外观模式,可是有的地方还须要直接 用到子系统。

ContractedBlock.gifExpandedBlockStart.gifCode
ExpandedBlockStart.gifContractedBlock.gif/**//// <summary>
        
/// 用户投票事件
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected void Button2_Click(object sender, EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif        
{
            facadeVote _facadeVote
=new facadeVote ();
            
this.Label1.Text = _facadeVote.userVote();

        }

 

    上面的外观类中采用了单件模式,可能有朋友会说在这里面可能是为了模式而模式,我想申明的是,本文属于个人观点,具体采用与否关键在于实际开发者。别人说的再多,再好,也比不过自己的实践。理论是实践的指导,而实践则是理论的见证。 

本文引用:

        http://dev.yesky.com/203/2175203.shtml

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值