外观模式
概述
外观模式可以为互相关联在一起的错综复杂的类整理出高层接口,其中Facade角色可以让系统对外只有一个简单的API。而且Facade角色还会考虑到系统内部各个类之间的责任关系和依赖关系,按照正确的顺序调用各个类。
外观模式中的Facade角色其实可以看成一个中间件,举一个比较熟悉的例子,在没有学习MyBatisPlus、MyBatis之前,我们对数据库的访问是使用JDBC的方式,而MyBatisPlus、MyBatis将内部调用的方法或类,整理出一个简单的API供我们使用。
示例程序
在示例程序中,我们编写简单的web页面
UML类图
名字 | 说明 |
---|---|
Database | 从邮件地址中获取用户名的类 |
HtmlWriter | 编写HTML文件的类 |
PageMaker | 根据邮件地址编写该用户的web页面 |
Test | 测试程序类 |
代码实现
Database
此类主要用于读取文件,生成properties
public class Database {
private Database() {//禁止外部使用new
}
/**
* 获取配置文件
* @param dbName
* @return
*/
public static Properties getProperties(String dbName) throws FileNotFoundException {
File file = ResourceUtils.getFile("classpath:" + dbName + ".txt");
Properties properties = new Properties();
try {
properties.load(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
return properties;
}
}
maildata.txt
yellowstar@qq.com=Yellow Star
max@qq.com=Max
HtmlWriter
此类主要用于编写html文件
public class HtmlWriter {
private Writer writer;
public HtmlWriter(Writer writer) {
this.writer = writer;
}
/**
* 输出标题
* @param title
*/
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");
}
/**
* 输出段落
* @param msg
*/
public void paragraph(String msg) throws IOException {
writer.write("<p>" + msg + "</p>\n");
}
/**
* 输出超链接
* @param href
* @param caption
* @throws IOException
*/
public void link(String href,String caption) throws IOException {
paragraph("<a href=\"" + href + "\">" + caption + "</a>");
}
/**
* 输出邮件地址
* @param mailaddr
* @param username
*/
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();
}
}
PageMaker
将Database和HtmlWriter包装成一个接口,提供给外部使用
public class PageMaker {
private PageMaker() {//禁止new
}
public static void makeWelcomePage(String mailaddr,String filename){
try {
Properties maildata = Database.getProperties("maildata");
String username = maildata.getProperty(mailaddr);
HtmlWriter htmlWriter = new HtmlWriter(new FileWriter(filename));
htmlWriter.title("Welcome to" + username + "`s page!");
htmlWriter.paragraph(username + " 欢迎来到" + username + "的主页");
htmlWriter.mailto(mailaddr,username);
htmlWriter.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
test
@SpringBootTest
class Practice1300ApplicationTests {
@Test
void contextLoads() {
PageMaker.makeWelcomePage("yellowstar@qq.com","welcome.html");
}
}
登场角色
-
Facade
Facade角色是代表构成系统的许多其他角色的简单窗口,Facade角色向系统外部提供高层接口API。在示例程序中由PageMaker扮演该角色
-
构成系统的许多其他角色
这些角色各自完成各自的工作,它们并不知道Facade角色,由Facade角色调用它们进行工作。在示例程序中由Database、HtmlWriter扮演该角色
习题
为了能够方便的对程序进行拓展和改善,作为设计者,我们想让pagemaker包外部的程序只能使用PageMaker类,而不能使用Database和HtmlWriter,如何改善该程序?(PageMaker、Database、HtmlWriter同属于pagemaker包)
将Database、HtmlWriter类前面的public去掉即可
class Database{}
class HtmlWriter{}
总结
-
外观模式可以让复杂的东西看起来简单,复杂的东西指的是在后台工作的这些类之间的关系和它们的使用方法,使用外观模式可以让我们不必在意这些复杂的东西。
-
接口变少了,意味着程序与外部的关联关系弱化了,这样更容易使我们的包作为组建被复用。
-
在设计类时,我们需要考虑将哪些方法的可见性设置为public,如果公开方法过多,会导致累的内部的修改变得困难。字段也是一样,如果不小心将某个字段公开出去,那么其他的类可能会读取或修改该字段。
-
在设计包时,也需要考虑类的可见性,如习题,我们不希望别人按照它们的思维修改内部的类的逻辑。