由于想深入一点了解Window Manager这一块,由于Window Manager用到了桥接模式,因此先了解一下桥接模式。Android 源码中存在大量的设计模式,而对于设计模式这一块的内容,我是属于看过就忘记的类型,虽然当初花过一段时间去看了很多设计模式,但是由于基本上工作中没有用到过所以时间长了就忘记了,这一块知识待补。
虽然设计模式这一块的文章写的人比较多,但是很多时候实际上由于个人水平以及理解能力的不同,导致不能很好地理解,由于我比较喜欢那种代码贴全了可以运行的sample,所以我觉得下面这篇文章例子举得比较好。
从Java类库看设计模式(3)
下面贴出代码以及简单解释。
出处:https://www.ibm.com/developerworks/cn/java/l-jdkdp/part3/index.html
下面,我们来看一个Bridge模式的具体应用。考虑这样的一个问题,需要生成一份报告,但是报告的格式并没有确定,可能是HTML文件,也可能是纯ASCII文本。报告本身也可能分为很多种,财务报表,货物报表,等等问题很简单,用继承也较容易实现,因为相互之间的组合关系并不是很多。但是,我们现在需要用Bridge的观点来看问题。
在Bridge模式中,使用一个Report类来描叙一个报告的抽象,用一个Reporter类来描叙Report的实现,它的子类有HTMLReporter和ASCIIReporter,用来分别实现HTML格式和ASCII格式的报告。在Report层次下面,有具体的一个StockListReport子类,用来表示货物清单报告。
public abstract class Report {
Reporter reporter;
public Report(Reporter reporter) {
this.reporter = reporter;
}
public void addReportItem(Object item){
reporter.addLine(item.toString());
}
public void addReportItems(List items){
Iterator iterator = items.iterator();
while ( iterator.hasNext() )
{
reporter.addLine(iterator.next().toString());
}
}
public String report(){
return reporter.getReport();
}
}
public abstract class Reporter {
String header = "";
String trailer = "";
String report = "";
public abstract void addLine(String line);
public void setHeader(String header){
this.header = header;
}
public void setTrailer(String trailer){
this.trailer = trailer;
}
public String getReport(){
return header+report+trailer;
}
}
public class StockListReport extends Report {
ArrayList stock=new ArrayList();
public StockListReport(Reporter reporter){
super(reporter);
}
public void addStockItem(String stockItem){
stock.add(stockItem);
addReportItem(stockItem);
}
}
public class ASCIIReporter extends Reporter {
public void addLine(String line) {
report += line + "\n";
}
}
public class HTMLReporter extends Reporter {
public HTMLReporter(){
setHeader("<HTML>\n<HEAD></HEAD>\n<BODY>\n");
setTrailer("</BODY>\n</HTML>");
}
public void addLine(String line){
report += line + "<BR>\n";
}
}
最后就是生成两种报告。
public class MainActivity extends AppCompatActivity{
private static final String TAG = "MainActivity";
private StockListReport mStockListReport1;
private StockListReport mStockListReport2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStockListReport1 = new StockListReport(new HTMLReporter());
mStockListReport1.addStockItem("apple :1");
mStockListReport1.addStockItem("orange :2");
mStockListReport2 = new StockListReport(new ASCIIReporter());
mStockListReport2.addStockItem("apple :1");
mStockListReport2.addStockItem("orange :2");
Log.d(TAG,"mStockListReport1 report = " + mStockListReport1.report());
Log.d(TAG,"mStockListReport2 report = " + mStockListReport2.report() );
}
}
生成报告结果如下:
<HTML>
<HEAD></HEAD>
<BODY>
apple :1<BR>
orange :2<BR>
</BODY>
</HTML>
apple :1
orange :2
出处:https://www.ibm.com/developerworks/cn/java/l-jdkdp/part3/index.html
实际上,Bridge模式是一个很强大的模式,可以应用在很多方面。其基本思想:分离抽象和实现,是设计模式的基础之一。正如GOF所提到的:"找到变化的部分,并将其封装起来";"更多的考虑用对象组合机制,而不是用对象继承机制"。Bridge模式很好的体现了这几点。
桥接模式的关键点在于:找到变化的部分,并将其封装起来。
至此,虽然对桥接模式有一定的了解,但是如果不使用桥接模式的话,直接生成不同格式的报告会怎么样?
public class ASCIIReporterTest {
String header = "";
String trailer = "";
String report = "";
public ASCIIReporterTest(){
}
public void addLine(String line){
report += line + "\n";
}
public String getReport(){
return header+report+trailer;
}
}
public class HTMLReporterTest {
String header = "";
String trailer = "";
String report = "";
public HTMLReporterTest(){
setHeader("<HTML>\n<HEAD></HEAD>\n<BODY>\n");
setTrailer("</BODY>\n</HTML>");
}
public void addLine(String line){
report += line + "<BR>\n";
}
public void setHeader(String header){
this.header = header;
}
public void setTrailer(String trailer){
this.trailer = trailer;
}
public String getReport(){
return header+report+trailer;
}
}
ASCIIReporterTest asciiReporterTest = new ASCIIReporterTest();
asciiReporterTest.addLine("apple :1");
Log.d(TAG,asciiReporterTest.getReport());
HTMLReporterTest htmlReporterTest = new HTMLReporterTest();
htmlReporterTest.addLine("apple :1");
Log.d(TAG,htmlReporterTest.getReport());
结果是
apple :1
<HTML>
<HEAD></HEAD>
<BODY>
apple :1<BR>
</BODY>
</HTML>
对比一下,如果不使用设计模式的话,在需要生成2种不同格式的报告的情况下,只需要直接新建2个类,然后使用,如果使用bridge设计模式的话,这里需要使用5个类。
缺点这里其实很明显了,增加了复杂度。
以下出处:https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/bridge.html#id11
桥接模式的缺点:
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。 - 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
那么桥接模式的优点在哪里?
此时如果不使用设计模式,需要新建不同的repoter类,并且需要重新定义内部细节,这种时候,设计模式的优点就显示出来了,虽然类的数量会稍微多一点,但是可以尽量减少改动,这种优势在数量多的情况下会更加明显(例如20个不同格式的报告类)。
下面以HTMLReporter 和HTMLReporterTest为例去对比分析。
public class HTMLReporterTest {
String header = "";
String trailer = "";
String report = "";
public HTMLReporterTest(){
setHeader("<HTML>\n<HEAD></HEAD>\n<BODY>\n");
setTrailer("</BODY>\n</HTML>");
}
public void addLine(String line){
report += line + "<BR>\n";
}
public void setHeader(String header){
this.header = header;
}
public void setTrailer(String trailer){
this.trailer = trailer;
}
public String getReport(){
return header+report+trailer;
}
}
public class HTMLReporter extends Reporter {
public HTMLReporter(){
setHeader("<HTML>\n<HEAD></HEAD>\n<BODY>\n");
setTrailer("</BODY>\n</HTML>");
}
public void addLine(String line){
report += line + "<BR>\n";
}
}
很明显,当使用桥接模式时,如果需要新建一个报告类,所需改动比不使用设计模式时少很多。如果是需要1个或2种不同的报告类,此时不使用设计模式可能还比较方便,但是当所需种类变多了之后,如果不使用设计模式的话就会变得比较麻烦,要新建大量的具有重复性的代码,而且不同格式报告类的差异没有那么一目了然。
这样看,桥接模式的优点也是很明显的。
但是,难点在于,如何找到变化的部分,并将其封装起来,在写代码的过程中,有时候并不能考虑到它的变化情况。
网友评论