Facade模式可以为相互关联在一起的错综复杂的类整理出高层接口,可以让系统对外只有一个简单的接口,而且还会考虑到系统内部各个类之间的责任关系和依赖关系,按照正常的顺序调用各个类。
还是先看一下示例程序的类图。
接下来根据示例程序代码理解一下Facade模式。
1 package bigjunoba.bjtu.facade; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.util.Properties; 6 7 public class Database { 8 9 private Database(){ //防止外部new出Database的实例,所以声明为private 10 } 11 12 public static Properties getProperties(String dbname) { 13 String filename = dbname + ".txt"; 14 Properties properties = new Properties(); 15 try { 16 properties.load(new FileInputStream(filename)); 17 } catch (IOException e) { 18 System.out.println("找不到文件" + filename); 19 } 20 return properties; 21 } 22 23 }
Database类就一个getProperties方法,使用这个方法可以通过传入的dbname来获取对应的Properties实例,properties字段保存的就是读取到的.txt文件。这里要注意的是getProperties是一个静态方法。
1 package bigjunoba.bjtu.facade; 2 3 import java.io.Writer; 4 import java.io.IOException; 5 6 public class HtmlWriter { 7 private Writer writer; 8 public HtmlWriter(Writer writer) { // 构造函数 9 this.writer = writer; 10 } 11 public void title(String title) throws IOException { // 输出标题 12 writer.write("<html>"); 13 writer.write("<head>"); 14 writer.write("<title>" + title + "</title>"); 15 writer.write("</head>"); 16 writer.write("<body> "); 17 writer.write("<h1>" + title + "</h1> "); 18 } 19 public void paragraph(String msg) throws IOException { // 输出段落 20 writer.write("<p>" + msg + "</p> "); 21 } 22 public void link(String href, String caption) throws IOException { // 输出超链接 23 paragraph("<a href="" + href + "">" + caption + "</a>"); 24 } 25 public void mailto(String mailaddr, String username) throws IOException { // 输出邮件地址 26 link("mailto:" + mailaddr, username); 27 } 28 public void close() throws IOException { // 结束输出HTML 29 writer.write("</body>"); 30 writer.write("</html> "); 31 writer.close(); 32 } 33 }
HtmlWriter类用于编写简单的web页面。在构造HtmlWriter类的实例时赋予其Writer,然后使用该Writer输出HTML。
1 package bigjunoba.bjtu.facade; 2 3 import java.io.FileWriter; 4 import java.io.IOException; 5 import java.util.Properties; 6 7 public class PageMaker { 8 private PageMaker() { // 防止外部new出PageMaker的实例,所以声明为private方法 9 } 10 public static void makeWelcomePage(String mailaddr, String filename) { 11 try { 12 Properties mailprop = Database.getProperties("maildata"); 13 String username = mailprop.getProperty(mailaddr); 14 HtmlWriter writer = new HtmlWriter(new FileWriter(filename)); 15 writer.title("Welcome to " + username + "'s page!"); 16 writer.paragraph("欢迎来到" + username + "的主页。"); 17 writer.paragraph("等着你的邮件哦!"); 18 writer.mailto(mailaddr, username); 19 writer.close(); 20 System.out.println(filename + " is created for " + mailaddr + " (" + username + ")"); 21 } catch (IOException e) { 22 e.printStackTrace(); 23 } 24 } 25 }
PageMaker类使用Database类和HtmlWriter类来生成指定用户的Web页面。makeWelcomePage方法会根据指定的邮件地址和文件名生成相应的Web页面。PageMaker类一手包办了调用HTMLWriter类的方法这一工作。对外部,它只提供了makeWelcomePage接口,这就是一个简单窗口。实现过程是,首先调用Database的getProperties方法读取.txt文件,然后生成一个Properties实例,将读取文件的结果保存在mailprop字段中,然后根据输入的邮箱地址通过getProperty方法获取对应的用户名。然后输出一系列Web页面的各个代码部分。
1 package bigjunoba.bjtu.facade; 2 3 public class Main { 4 public static void main(String[] args) { 5 PageMaker.makeWelcomePage("sjy534948129@sina.com", "welcome.html"); 6 } 7 }
Main类输入文件中保存的邮箱地址和输出文件名来生成一个Web页面。
sjy534948129@sina.com=BigJunOba 17125031@bjtu.edu.cn=Lianjiang
maildate.txt保存了两条数据。
1 <html><head><title>Welcome to BigJunOba's page!</title></head><body> 2 <h1>Welcome to BigJunOba's page!</h1> 3 <p>欢迎来到BigJunOba的主页。</p> 4 <p>等着你的邮件哦!</p> 5 <p><a href="mailto:sjy534948129@sina.com">BigJunOba</a></p> 6 </body></html>
welcome.html输出文件。
Facade模式类图。
解释一下这些部分:
Facade:代表构成系统的许多其他部分的“简单窗口”。向外部提供高层接口。示例程序中的PageMaker就是这个。
其他类:这些类各自完成自己的工作,但是只能是Facade来调用这些其他类工作。示例程序中Database和HtmlWriter类就是这个情况。
Client:示例程序中的Main只能调用Facade的一个接口,这样就减少了接口的数量。
接口变少的优势:
程序中如果有很多类和方法,那么在决定到底应该使用哪个类或方法时就会很容易迷茫。类和方法的调用顺序也很容易弄错。然后接口变少可以解决这个问题。
同时,接口变少还意味着程序与外部的关联关系弱化了,这样更容易使我们的包作为组件被复用。
还要尤其注意的是,在设计类时,要将哪些方法的可见性设为public。如果公开的方法过多,那么类的内部的修改会变得困难。如果公开了某个字段,那么其他类可能会读取或是修改这个字段,导致难以修改该类。如果公开了包,那么让外部看到了类,包内的代码的修改就会变得困难。