图解设计模式
设计模式
predefinition
UML
UML是让系统可视化、让规格和设计文档化的表现方法,全称为Unified Modeling Language
类
UML中的类图用于表示类、接口、实例等之间相互的静态关系。
类与层次结构
上图展示了ParentClass和ChildClass两个类之间的关系,空心箭头表明了两者之间的层次关系,箭头由子类指向父类,即继承。反过来说,ChildClass是ParentClass的子类。父类也成为基类或者超类,子类也成为派生类。
图中的长方形表示类,内部被横线自上而下分为类名、字段名、方法名。子类一定指向父类
接口与实现
带有空心三角形的虚线箭头代表了接口与实现类的关系,箭头从实现类指向接口。
聚合
只要在一个类中持有另外一个类的实例,无论是一个还是多个,它们之间的关系就是聚合关系。UML中使用带有空心菱形的实线表示聚合关系。
可见性(访问控制)
+
表示public方法和字段,可以从类外部访问这些方法和字段
-
表示private方法和字段,无法从类外部访问这些方法和字段
#
表示protected方法和字段,能够访问这些方法和字段的只能是该类自身、该类的子类以及同一包中的类
~
表示同一包中的类才能访问的方法和字段
类的关联
可以在类名前面加上黑三角表示类之间的关联关系。
时序图
UML的时序图用来表示程序在工作时其内部方法的调用顺序,以及事件的发生顺序。
类图中表示的是“不因时间流失而发生变化的关系”,时序图则与之相反,表示的是“随时间发生变化的东西”。
处理流与对象间的协作
client、server、device是三个实例,向下延伸的线是生命线。生命线仅存在于实例的生命周期内。黑色实线箭头代表方法的调用,虚线箭头代表返回open方法。时序图的阅读顺序是沿着生命线从上至下阅读。遇到箭头时,顺着箭头所指的方向查看对象间的协作。
Iterator模式
Iterator模式用于在数据集合中按照顺序遍历集和。
示例
Aggerate 接口中只声明了一个iterator
方法,该方法会生成一个用于遍历集和的迭代器。
// Aggerate 接口
public interface Aggregate{public abstract Iterator iterator();
}
Iterator 接口用于遍历集合中的元素,作用相当于循环语句中的循环变量。hasNext()
用于判断是否存在下一个元素,next()
用于获取下一个元素。
// Iterator 接口
public interface Iterator{public abstract boolean hasNext();public abstract Object next();
}
Book类是表示书的实体类。
// Book 类
public class Book{private String name;public Book(String name){this.name = name;}public String getName(){return name;}
}
BookShelf是表示书架的实体类,实现了Aggerate接口以及其中的iterator
方法。
// BookShelf 类
public class BookShelf implements Aggerate{private Book[] books;private int last = 0;public BookShelf(int maxsize){this.books = new Book[maxsize];}public Book getBookAt(int index){return books[index];}public void appendBook(Book book){this.books[last] = book;last++;}public int getLength(){return last;}public Iterator iterator(){return new BookShelfIterator(this);}
}
BookShelfIterator类用于遍历书架,需要发挥Iterator
的作用,实现了Iterator
接口。bookShelf
字段是BookShelfIterator所要遍历的书架,index字段表示迭代器当前所指向的书的下标。
// BookShelfIterator 类
public class BookShelfIterator implements Iterator{private BookShelf bookshelf;private int index;public BookShelfIterator(BookShelf bookShelf){this.bookShelf = bookShelf;this.index = 0;}public boolean hasNext(){if (index < bookShelf.getLength()){return true;} else{return false;}}public Object next(){Book book = bookShelf.getBookAt(index);index++;return book;}
}
使用Main类来执行所有代码。
public class Main {public static void main(String[] args) {BookShelf bookShelf = new BookShelf(4);bookShelf.appendBook(new Book("Around the World in 80 Days"));bookShelf.appendBook(new Book("Bible"));bookShelf.appendBook(new Book("Cinderella"));bookShelf.appendBook(new Book("Daddy-Long-Legs"));Iterator it = bookShelf.iterator();while (it.hasNext()) {Book book = (Book)it.next();System.out.println(book.getName());}}
}
上述代码设计了能容纳4本书的书架,然后按照书名的英文字母顺序添加进入书架内。通过bookShelf.iterator()
得到的实例it
是用于遍历书架的Iterator实例。
解析
Iterator中出现的角色包括Iterator
、ConcreteIterator
、Aggregate
、ConcreteAggregate
。
-
Iterator
定义按顺序逐个遍历元素的API。
-
ConcreteIterator
实现Iterator所定义的API。
-
Aggregate
定义创建Iterator的API。
-
ConcreteAggregate
实现Aggregate所定义的API。
类图如下:
**引入Iterator后可以将遍历与实现分离开来。**无论实体类如何变化,只要iterator
方法能正确地返回实例,即使不对核心代码(while循环)做任何修改,代码都可以正常工作。这也就是设计模式的一个重要作用:帮助编写可复用的类。
如果只是用具体类来解决问题,容易导致类之间的强耦合,这些类也难以被作为组件再次利用,为了弱化类之间的耦合,使得类更容易作为组件被再次利用,需要引入抽象类和接口。
在Java中,没有使用的对象实例会自动被删除(垃圾回收)。因此在iterator中不需要与其对应的deleteIterator方法。
Adapter模式
用于填补“现有的程序”和“所需的程序”之间差异的设计模式就是Adapter模式。Adapter模式也被称为Wrapper模式。
Adapter有以下两种:
- 类适配器模式(使用继承的适配器)
- 对象适配器模式(使用委托的适配器)
示例
扮演适配器角色的是PrintBanner
,该类继承了Banner
并实现了需求——Print
接口。
Banner类是当前已有的类
// Banner 类
public class Banner{private String string;public Banner(String string){this.string = string;}public void showWithParen(){System.out.println("(" + string + ")");}public void showWithAster(){System.out.println("*" + string + "*");}
}
Print接口时需求的接口
// Print 接口
public interface Print{public abstract void printWeak();public abstract void printStrong();
}
PrintBanner类扮演适配器的角色,继承了Banner类,实现了Print接口。
// PrintBanner 类
public class PrintBanner extends Banner implements Print{public PrintBanner(String string){super(string);}public void printWeak(){showWithParen();}public void printStrong(){showWithAster();}
}
Main类通过适配器PrintBanner类来进行任务执行
public class Main{public static void main(String[] args){Print p = new PrintBanner("Hello");p.printWeak();p.printStrong();}
}
类适配器的优点:直接通过继承现有类和实现目标接口来完成适配,代码简洁。缺点:由于Java不支持多重继承,如果现有类不是接口,则无法同时继承另一个类来扩展功能。同时与现有类之间存在强耦合,如果现有类发生更改,适配器也要做相应的更改。
Java中,委托指将某个方法中的实际处理交给其他实例的方法。
Main类和Banner与上面的代码相同,我们假设Print不是接口而是类。利用Banner类实现一个类,该类的方法和Print类的方法相同。
// Print 接口
public abstract class Print{public abstract void printWeak();public abstract void printStrong();
}
// PrinBanner 类
public class PrintBanner extends Print{private Banner banner;public PrintBanner(String string){this.banner = new Banner(string);}public void printWeak(){banner.showWithParen();}public void printStrong(){banner.showWithAster();}
}
解析
Adapter模式中有以下角色:
-
Target
定义所需的方法
-
Client
使用Target角色所定义的方法进行具体处理
-
Adaptee
持有既定方法的角色
-
Adapter
使用Adaptee角色的方法来满足Target角色的需求,这是Adapter模式的目的,也是Adapter角色的作用。
类图如下:
继承:
使用委托
什么时候使用Adapter模式:
Adapter模式会对现有的类进行适配,生成新的类。通过该模式可以很方便地创建我们需要的方法群。当出现bug时,由于我们很明确地知道bug不在现有的类(adaptee)中,所以只需要调查Adapter的类即可。
使用Adapter模式可以在完全不修改现有代码的前提下使现有代码适配于新的接口。在Adapter模式中也并非一定需要现成的代码,只要知道现有类的功能,就可以编写出新的类。
----------------------------------------------------------未完待续------------------------------------------------------
Reference
图解设计模式 【日】结成浩 著 杨文轩 译