一、Facade 模式
1. 需求
一个用于从邮件地址中获取用户名字的数据库类,一个用于编写html 文件的类,以及一个半阳 facade 角色并提供高层接口API 的类。
2. UML
3. Code
大家看明白模式的设计思路就好,但是编码的示例比较古老,就不推荐大家学习了。
文件的目录结构:
3.1 DataBase 模拟从数据库中获取数据信息
/**
* @author CSZ
*/
public class DataBase {
private DataBase() {
}
public static Properties getProperties(String dbname){
String filename = dbname + ".properties";
Properties properties = new Properties();
try {
properties.load(new FileInputStream(filename));
}catch (IOException e){
e.printStackTrace();
}
return properties;
}
}
3.2 HtmlWriter 用于编写 html
/**
* @author CSZ
*/
public class HtmlWriter {
private Writer writer;
public HtmlWriter(Writer writer) {
this.writer = writer;
}
public void title(String title) throws IOException{
writer.write("<html>");
writer.write("<head>");
writer.write("<title>" + title +"</title>");
writer.write("</head>");
writer.write("<body>\n");
writer.write("<h1>" + title + "</h1>/n");
}
public void paragraph(String msg) throws IOException{
writer.write("<p>" + msg + "</p>\n");
}
public void link(String href,String caption) throws IOException{
paragraph("<a href=\"" + href + "\">" + caption + "</a>");
}
public void mailto(String mailaddr,String username)throws IOException{
link("mailto:" + mailaddr,username);
}
public void close() throws IOException{
writer.write("</body>");
writer.write("</html>/n");
writer.close();
}
}
3.3 PageMaker 最终组装,也是 facade 的体现
/**
* @author CSZ
*/
public class PageMaker {
private PageMaker(){}
public static void makeWelcomePage(String mailaddr,String filename){
try {
Properties maildata = DataBase.getProperties("maildata");
String username = maildata.getProperty(mailaddr);
HtmlWriter writer = new HtmlWriter(new FileWriter(filename));
writer.title("Welcome to" + "username" + "'s page");
writer.paragraph(username + "欢迎来到" + username + "的主页");
writer.paragraph("等着你的邮件");
writer.mailto(mailaddr,username);
writer.close();
System.out.println(filename + "is created for" + mailaddr + "(" + username + ")");
}catch (IOException e){
e.printStackTrace();
}
}
}
3.4 模拟的数据库文件
qqqq@qq.com=qqqq
wangyi@wangyi.com=wangyi
google@gmail.com=google
apple@apple.com=apple
3.5 测试类
/**
* @author CSZ
*/
public class MainTest {
public static void main(String[] args) {
PageMaker.makeWelcomePage("apple@apple.com","welcome.html");
}
}
总结与思考
-
这种设计模式的作用:个人理解着很像我们常用的 工具类,StringUtils BeanUtils 等等,这样就减少了我们呢对于 接口API 的调用。
- 当程序中,我们需要判断是否包含空格时,我们可以自己写一个方法,逻辑跟里面的逻辑也会差不多,但是如果他们已经提供好了现成的方法,我们直接调用是不是更加简单。这样我们本来需要调用 n 个工具包中的方法,并且编写逻辑,而现在我们只需要直接调用 contiansWhitespace 就可以了。
- 再进一步讲,在设计之初的时候,他们可能设计好了许多内在的关联关系,调用逻辑。但是作为使用者的你,可能并不知道他们做了怎样的优化,那么干脆提供给你一个他们已经实现好的复合逻辑,帮你判断了一些极端情况,而且有更高的执行效率。
- 反过来讲,书中也提出了,作为程序员,我们不喜欢利用这种模式做设计,原因是,如果我们的设计有漏洞,那么调用者就会遭殃,干脆就散落一些原子方法在类中。
- 另一个问题就是我们的类,字段,方法的暴露问题:如果我们暴露了太多,当我们作为设计者,想再修改这个类的内部就会很吃力,因为,别人能够看到的方法,别人可能调用哦,项目不是你一个人再做,一个开源的项目,你一个变动,可能很多项目就无法运行了,这是不合理的。
- 再代码的关键处,我们使用这种模式可以递归调用 Facade 模式,即 Facade 模式中封装的又是 Facade 模式的集合。
二、Mediator 模式
1. 说明:
2. 场景
设计一个会因为情况变动而变动的窗口。
3. UML
4. Code
4.1 Mediator
/**
* 用于声明仲裁者身份
* @author CSZ
*/
public interface Mediator {
/**
* 用于生成 Mediator 要管理的组员
*/
public abstract void createColleagues();
/**
* 用于组员向仲裁者报告情况
*/
public abstract void colleagueChanged();
}
4.2 Colleague
/**
* 用于声明组员身份
* @author CSZ
*/
public interface Colleague {
/**
* 向仲裁者进行报告的组员的接口,由具体组员实现
*/
public abstract void setMediator(Mediator mediator);
/**
* 再根据所有组员的情况得出结论后,给每个组员指定要求
*/
public abstract void setColleagueEnabled(boolean enabled);
}
4.3 ColleagueButton (组员)
/**
* 组员1
* @author CSZ
*/
public class ColleagueButton extends Button implements Colleague{
private Mediator mediator;
public ColleagueButton(String label) throws HeadlessException {
super(label);
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void setColleagueEnabled(boolean enabled) {
setEnabled(enabled);
}
}
4.4 ColleagueCheckbox (组员)
/**
* 组员2
* @author CSZ
*/
public class ColleagueCheckbox extends Checkbox implements ItemListener,Colleague{
private Mediator mediator;
public ColleagueCheckbox(String label, boolean state, CheckboxGroup group) throws HeadlessException {
super(label, state, group);
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void setColleagueEnabled(boolean enabled) {
setEnabled(enabled);
}
@Override
public void itemStateChanged(ItemEvent e) {
mediator.colleagueChanged();
}
}
4.5 ColleagueTextFiled (组员) 其他组员类似,在最后一个组员中说明方法作用
/**
* 组员3
* @author CSZ
*/
public class ColleagueTextFiled extends TextField implements TextListener,Colleague{
private Mediator mediator;
public ColleagueTextFiled(String text, int columns){
super(text, columns);
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
// 在仲裁者给出的意见,要求该成员调用下面方法,完成响应
@Override
public void setColleagueEnabled(boolean enabled) {
setEnabled(enabled);
setBackground(enabled?Color.white:Color.lightGray);
}
// 当文字域中发生改变,被监听到,去找仲裁者仲裁
@Override
public void textValueChanged(TextEvent e) {
mediator.colleagueChanged();
}
}
4.6 LoginFrame (仲裁者)
/**
* 类声明部分,通过实现 Mediator 来说明自己将担任仲裁者的身份
* @author CSZ
*/
public class LoginFrame extends Frame implements ActionListener,Mediator {
// 这里内部属性就是所有仲裁者小组下的员工
private ColleagueCheckbox checkGuest;
private ColleagueCheckbox checkLogin;
private ColleagueTextFiled textUser;
private ColleagueTextFiled textPass;
private ColleagueButton buttonOk;
private ColleagueButton buttonCancel;
// 构造方法将所有的组件创建
public LoginFrame(String title) throws HeadlessException {
super(title);
setBackground(Color.lightGray);
setLayout(new GridLayout(4,2));
// 实现 Mediator 就要指明谁是你的员工
createColleagues();
add(checkGuest);
add(checkLogin);
add(new Label("Username:"));
add(textUser);
add(new Label("Password:"));
add(textPass);
add(buttonOk);
add(buttonCancel);
colleagueChanged();
pack();
show();
}
// 对于 Mediator 来说要指明员工
@Override
public void createColleagues() {
// 类似构建 form 表单
CheckboxGroup checkboxGroup = new CheckboxGroup();
checkGuest = new ColleagueCheckbox("Guest", true, checkboxGroup);
checkLogin = new ColleagueCheckbox("Login", false, checkboxGroup);
textUser = new ColleagueTextFiled("", 10);
textPass = new ColleagueTextFiled("", 10);
textPass.setEchoChar('*');
buttonOk = new ColleagueButton("OK");
buttonCancel = new ColleagueButton("Cancel");
// 对于每个员工来说他们要重写 setMediator 方法来指定他们的组长
checkGuest.setMediator(this);
checkLogin.setMediator(this);
textUser.setMediator(this);
textPass.setMediator(this);
buttonOk.setMediator(this);
buttonCancel.setMediator(this);
// 为了程序的效果,添加监听器
checkGuest.addItemListener(checkGuest);
checkLogin.addItemListener(checkLogin);
textUser.addTextListener(textUser);
textPass.addTextListener(textPass);
buttonOk.addActionListener(this);
buttonCancel.addActionListener(this);
}
// 每个在员工身上的监听器,当自己发生变化后,会调用仲裁者的下面方法来请求解决方案
@Override
public void colleagueChanged() {
if (checkGuest.getState()){
textUser.setColleagueEnabled(false);
textPass.setColleagueEnabled(false);
buttonOk.setColleagueEnabled(true);
}else {
textUser.setColleagueEnabled(true);
userpassChanged();
}
}
private void userpassChanged(){
if (textUser.getText().length() > 0){
textPass.setColleagueEnabled(true);
buttonOk.setColleagueEnabled(textPass.getText().length() > 0);
}else {
textPass.setColleagueEnabled(false);
buttonOk.setColleagueEnabled(false);
}
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.toString());
System.exit(0);
}
}
4.7 测试类
/**
* @author CSZ
*/
public class MainTest {
public static void main(String[] args) {
new LoginFrame("Mediator Sample");
}
}
5. 分析与总结
- 这个设计模式还是很有趣的,大家可以直接将我的代码拷过去,不要在乎java.awt 的实现细节。还是把关注点放在思想上。
- 问题就出在这里,思想的核心在于,牵一发而动全身,我们对组件中每个位置的修改,都会导致全盘的逻辑都需要调整,这就是关键,试想一下,如果我们不用这个模式,那么我们是不是要把所有的情况全部在if-else 中体现出来,比如 A开,B开,C开,所以 ok 开,cancle开,A 开,B开,C不开,所以ok不开,cancle开。。。这样逻辑上重复,臃肿的代码就在 ABC三个类中。现在呢,我们通过仲裁者的模式,当ABC中任意一个变动,我们本身都不直接处理,我们将所有的情况综合起来,汇报交给仲裁者,仲裁者根据一套逻辑,直接指明,ok 和 cancle 的状态,(其实,仲裁者会把每个人的状态都做规定,并且每个人都会执行,只是在这里我们假设关注 ok cancle状态)。
- 另外,一个组员跑路了,我们可以用新的组员来补充,或者我们简化团队,但是,项目经理跑路了,项目可能就凉了,望周知(bushi /doge)。