起因
设计模式在运用中愈加明白,在交流中不断提升,在敲机房个人重构的时候,外观模式和抽象工厂模式是肯定要用到的就是为为了进行解耦,当时并不理解外观模式究竟是起到怎样的一个模式,就借鉴着开始了,到了后来发现整个外观模式已经被架空了,毫无起到解耦的作用,对于书上的理论还是无法理解,无意中在交流中看到了鑫超哥的代码,恍然大悟书上的理论也明白了,下面便是自己的一些收获
上图
是什么
定义:为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。具体的理解以代码说明,不过这两句话可以在理论上起到帮助
1.Facade为子系统提供一个对外的共同接口;2.客户端通过这个接口去读取子系统中的数据资源(在机房收费中数据资源就是B层中的方法来获取具体信息的)
作用
1.facade模式最大的作用的就是解耦分层,这样客户端虽然不知道具体的用到了facade中哪个子系统的方法数据,但不影响功能的实现,这既简便也符合开放封闭原则
2.在添加新的逻辑判断时U层可以完全解放,只需在B层定义个方法来供facade接受,如等我们登录时还需要将用户相关信息放到正在值班教师表中,我们只需把U层的相关信息传入在facade加一个update就可以了,具体的实现就交给B层了
UML图
没用facade模式之前的图
子系统修改客户端也得进行相关修改,而且客户端还要不断考虑是否应该调用该子系统
用了facade模式的UML
这时我们发现无论子系统在怎么复杂,客户端只需要调用一个外观层就够了,具体的逻辑功能的实现与判断只需子系统工作完后放到facade层就可以了
实例UML(机房收费登录窗体)
可以看到当登录时要判断是否为空,是否存在,是否密码正确,在U层只需调用facade中的checkInput足以,至于具体的逻辑判断都在B层进行的,然后将结构返回到facade,这时调用了facade的U层便可以显示结果了
如何用
当一个功能的实现要经过N多个小逻辑前提的实现才能满足的时候就可以考虑该模式了,如上机时,要判断卡号是否存在,是否正在是否还在使用,余额是否够用,是否正在上机等才能进行ONline,这是用facade模式写一个Online这个方法U层只需把cardInfo信息传过来就可以了。如果一个功能就只需满足一个条件如正在值班教师只要表里有数据就行这是就没有必要用外观模式了
理论上的使用条件:
误区
<span style="color:#333333;">'U层
Private Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
'进行非空判断
If txtPassWord.Text = "" Then
MsgBox("旧密码不能为空")
txtPassWord.Focus()
Exit Sub
End If
If txtNewPassWord.Text = "" Then
MsgBox("新密码不能为空")
txtNewPassWord.Focus()
Exit Sub
End If
'对新密码进行限定只能是数字
If IsNumeric(txtNewPassWord.Text) = False Then
MsgBox("请输入数字")
txtNewPassWord.Text = ""
txtNewPassWord.Focus()
Exit Sub
End If
'判断旧密码是否正确
</span><span style="color:#ff0000;"> Dim ModifyFAC As New Facade.LoginFAC '借用了登录窗体的密码判断的代码
Dim UserInfo As New Entity.EntityUsersWork
UserInfo.userID = frmLogin.txtUserName.Text</span><span style="color:#333333;">
Dim table As New DataTable
table = ModifyFAC.loginInfo(UserInfo)
If txtPassWord.Text = Trim(table.Rows(0).Item(1)) Then
MsgBox("旧密码正确")
Else
MsgBox("旧密码错误")
txtPassWord.Text = ""
txtPassWord.Focus()
Exit Sub
End If
'判断两次密码是否一样
If txtNewPassWord.Text <> txtNew1.Text Then
MsgBox("两次密码不一致请重新确认")
txtNew1.Text = ""
txtNew1.Focus()
Exit Sub
Else
'进行密码更新
Dim updateFAC As New Facade.ModifyFAC
Dim table1 As New Boolean
UserInfo.pwd = txtNew1.Text
</span><span style="color:#ff0000;"> table1 = updateFAC.ModifyPwd(UserInfo)</span><span style="color:#333333;">
MsgBox("修改成功")
End If
End Sub
'Facade层
</span><span style="color:#ff0000;">Public Function ModifyPwd(ByVal UserInfo As Entity.EntityUsersWork) As Boolean</span><span style="color:#333333;">
Dim ModifyBLL As New ModifyPwdBLL
Dim table As New Boolean
'定义一个方法引用B层
table = ModifyBLL.ModifyPwd(UserInfo)
Return table
End Function
</span>
如果单纯的从实现代码功能来看,这段代码是没有任何问题的,然而仔细看后就会发现,我用了facade层之后不仅没有实现解耦,还令U层与facade层,facade层与B层紧密的链接在了一起,和U层直接调用B层没有区别。而且我还调用了其他功能的方法,使得层与层,功能与功能之间混乱了
正常用facade层应该是为多个小功能的判断整合成一个大的接口供U层调用,所以我陷入的误区便是仅仅把Facade层理解成了U层和B层的过度层,而不死解耦层,即令U层和Facade层紧密结合,Facade层和U层紧密结合,U层和层不是解耦而是没有关系了。(主要看红色的代码)
正解实例
以登录窗体为例
<span style="color:#333333;">U层
Private Sub btnOK_Click(sender As System.Object, e As System.EventArgs) Handles btnOK.Click
Try
Dim facade As New Facade.LoginFacade
Dim UserInfo As New Model.LoginUserInfo
UserInfo.UserName = txtUser.Text.Trim
UserInfo.Password = txtPassword.Text
Model.LoginUserInfo.User = txtUser.Text.Trim
Dim strCheckResult As String
</span><span style="color:#ff0000;">strCheckResult = facade.CheckInput(UserInfo)'通过checkInput将信息传入就静待其变了,不用去考虑是怎么实现的</span><span style="color:#333333;">
If strCheckResult = "OK" Then
MsgBox("你可以成功登陆啦~~~", MsgBoxStyle.OkOnly, "来自软件欢乐的提示 ")
Dim frmmain As New frmMain
frmmain.Show()
Else
Call ClearTxt()
MsgBox(strCheckResult, MsgBoxStyle.OkOnly, "来自软件热心的提示 ")
End If
Catch ex As Exception
MsgBox("UI层-登陆窗体 出现错误啦~~~~")
Call ClearTxt()
End Try
End Sub
Facade层
Public Function CheckInput(ByVal UserInfo As Model.LoginUserInfo) As String
</span><span style="color:#ff0000;">Dim Checker As New BLL.LoginBll'具体的实现就交给了B层去判断了,并把判断结果返回到Facade层</span><span style="color:#333333;">
Dim result As String
Dim result1 As String
Dim result2 As String
Dim result3 As String
</span><span style="color:#ff0000;"> result1 = Checker.IsNothing(UserInfo)
result2 = Checker.CheckUser(UserInfo)
result3 = Checker.CheckPassword(UserInfo)
result = Checker.GetAnswer(result1, result2, result3)
Return result</span><span style="color:#333333;">
End Function
</span>
此处说明一点:一般我们都是传实体,返回的有table型,boolean型,泛型。当乍一看到string时有点蒙,其实传什么型的参数和要返回什么的数据是没有什么直接关系的,只要根据需要做好判断和转化就行
总结
关于外观模式学习一开始没有了解参考别人的也断章取义导致完全用错,在交流中及时发现了错误明白了Facade模式是为多个子系统提供一个统一的接口,如何功能并不复杂而不必考虑此模式