概述
设计模式(Design Pattern)
使用设计模式可以有效的提高开发效率和软件的健壮性等特性。设计模式可以分为下面几类:
- 结构型模式
- 行为类模式
- 创建型模式
设计模式有很多种,本文会给出一些有代表性的设计模式。
结构型模式:
- Adapter 适配器模式
- Decorator 装饰器模式
- Façade 外观模式
- Proxy 代理模式
行为类模式:
- Strategy 策略模式
- Template Method 模板模式
- Iterator 迭代器模式
- Observer 观察者模式
- Visitor 访问者模式
创建型模式:
- Factory method 工厂方法模式
- Abstract factory 抽象工厂模式
在给出设计模式之前,先给出委托的概念。
委托(delegation)
委托的作用
委托是一项提高软件复用性的技术,相较于继承机制,委托更加灵活多变,能够有效的降低耦合度。很多设计模式都是讲继承和委托结合使用。委托发生在对象层面,而继承发生在类层面。
委托的实现
总的思路是:把A的功能委派给B来实现。
结构框图:
例子:
由于动物有多个行为属性,而每个属性又有多个取值,这个需求如果使用继承实现,会导致大量的组合。
下面给出使用委托机制的实现
// 定义两个抽象的行为接口
interface Flyable{
public void fly();
}
interface Quackable{
public void quack();
}
//再给出两个接口的实现,每个接口可以有多个实现,但这里每个接口只给出一个实现
class FlyWithWings implements Flyable{
@override
public void fly(){
System.out.println("fly with wings");
}
}
class Quack implements Quackable{
@override
public void fly(){
System.out.println("quack like duck");
}
}
上面的这些代码实际上是设计了被委托对象(也就是委托中的B)。下面再给出A如何进行委托。
interface Ducklike extends Flyable,Quackable{
//这里通过接口的组合,定义了行为的组合
}
public class Duck implements Ducklike{//这里的Duck类通过继承Ducklike接口,拥有了相应的行为
//这里是在设置委托。这里直接进行了绑定,是委托中的组合(Composition),还有其他的几种委托,将在下面给出。
Flyable flyBehavior = new FlyWithWings();
Quackable quackBehavior = new Quack();
//这里通过委托来实现具体的行为
@override
public void fly(){
this.flyBehavior.fly()
}
@override
public void quack(){
this.quackBehavior.fly()
}
}
接下来给出客户端使用方法。
//客户端的使用很简单
Duck d = new Duck();
d.fly();
d.quack();
委托的分类
委托根据具体的实现还可以分为几类。
- 依赖(Dependency):A use B
- 关联(Association):A has B
- 组合(Composition)/聚合(aggregation):A owns B4
其中,组合和聚合可以视作关联的两种具体形态。
下面解释这几个类型的区别。
Dependency:临时的委托
通过方法的参数或者在方法的局部中使用时发生联系。
class Duck{
//这里不再保存Flyable对象
void fly(Flyable f){
//通过外部传入参数来进行委托
f.fly();
}
}
//外部调用的时候,需要传入一个参数
Flyable f = new FlyWithWings();
Duck d = new Duck();
d.fly(f);
从上面的代码可以看出,Duck类中不再保存对应的被委托者(B)的对象,转而使用传入参数来进行委托。所以这个委托只是临时的。
Association:永久性的委托
与上面的临时性委托相反,这里A会保存B的对象,从而达到建立永久性的联系。
Association可以分为Composition和Aggregation,两者的区别是,前者保存被委托者的对象时直接绑定了具体的对象,而后者仅仅是声明,未进行绑定。
Composition:
class Duck{
//这里直接进行了绑定
Flyable f = new FlyWithWings();
void fly(){
f.fly();
}
}
Aggregation:
class Duck{
//这里仅仅声明了变量
Flyable f;
//这里根据外界传入的参数来进行绑定
void Duck(Flyable f){
this.f = f;
}
void fly(){
f.fly();
}
}
Adapter 适配器模式
这个模式用来解决接口不兼容的问题。通过增加一个接口,将已存在的子类封装起来,然后client面向这个新增的接口进行编程,这样既解决了不兼容问题,也隐藏了具体子类。
结构框图:
适配器模式可以通过继承和委托两种方式实现。
例子:
一个LegacyRectangle类有一个display()方法,接收参数是左上角坐标+宽+高。但是客户端想接收左上角坐标+右下角坐标。这样就造成了不兼容的问题。我们使用适配器模式来解决这个问题。
设计图:
代码:
interface Shape{
//client期望的参数列表形式
void dispaly(int x1, int y1, int x2, int y2);
}
class LegacyRectangle{
//我们实际的参数列表形式,和上面的不兼容
void dispaly(int x1, int y1, int w, int h){
...
}
}
class Rectangle implements Shape{//Adaptor类实现抽象接口
void display(int x1, int y1, int x2, int y2){
new LegacyRectangle().display(x1, y1, x2-x1, y2-y1);
}
}
class Client { //客户端可以直接对抽象接口进行编程
Shape shape = new Rectangle();
public display() {
shape.display(x1, y1, x2, y2);
}
}
Decorator 装饰器模式
这个模式主要用来解决子类特征组合的问题。对每一个特征构造子类,通过委托机制增加到对象上。
设计框图:
其中Decorator抽象类是所有装饰类的基类,里面包含的成员变量component指向了被装饰的对象。
例子:
对于一个最基本的Stack类,实现具有多种功能的子类。如SecureStack、UndoStack、SynchronizedStack。
设计图:
代码:
下面的代码是实现一个最基本的Stack类,当作被装饰物。
interface Stack {
void push(Item e);
Item pop();
}
public class ArrayStack implements Stack { //实现一个最基本的Stack类,当作被装饰物
... //rep
public ArrayStack() {...}
public void push(Item e) {
...
}
public Item pop() {
...
}
...
}
再写Decorator抽象类
//给出一个用于 decorator的基础类
public abstract class StackDecorator implements Stack {
protected final Stack stack; //这个成员变量用来保存被装饰对象
public StackDecorator(Stack stack) {
this.stack = stack;
}
//下面的Stack基础功能仍然是通过委托实现
public void push(Item e) {
stack.push(e);
}
public Item pop() {
return stack.pop();
}
...
}
下面给出具体的装饰,只给出一个,其他的特征写法类似
public class UndoStack extends StackDecorator {
private final UndoLog log = new UndoLog();
public UndoStack(Stack stack) {
super(stack);
}
public void push(Item e) {
super.push(e); //基础功能通过委托实现
log.append(UndoLog.PUSH, e);//增加新特性
}
public void undo() {//增加新特性
//实现修饰的功能
...
}
}
再给出使用方法
Stack s = new ArrayStack();//创建基础类
Stack t = new UndoStack(new ArrayStack());//创建一个具undo特征的stack
Stack t = new SecureStack(new SynchronizedStack( new UndoStack(s)))//创建一个多特征的Stack
Façade 外观模式
Façade模式很简单,目的是提供一个统一的接口来取代一系列小接口调用,相当于对复杂 系统做了一个封装,简化客户端使用。
设计框图:
这个模式很简单就不写了。
Proxy 代理模式
某个对象比较“敏感”“私密”/“贵重”,不希望被 client直接访问到,故设置 proxy,在二者之间建立防火墙。这个模式和适配器模式相似,但是针对的应用场景不同,适配器主要解决的是Client和ADT之间的接口不兼容问题,代理模式是为了隔离对复杂对象的访问,降低访问的代价。
设计框图:
例子:
从磁盘加载图片,有时候仅仅只是需要图片的对象,不需要真正的从磁盘加载图片,如果直接创建Image对象会产生很高的代价。
代码:
public interface Image{
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {…}
private void loadFromDisk(String fileName){…}//每次创建都要从磁盘加载,代价高
}
public class ProxyImage implements Image {
private Image realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;//这里创建一个Image就不需要从磁盘加载
}
@Override
public void display() {//调用display,真正用到Image时候再开始加载
if(realImage == null){
realImage = new RealImage(fileName);//通过委托机制
}
realImage.display();
}
}
Strategy 策略模式
这个模式也很简单,就是有多种不同的算法来实现同一个任务,但需要client根据需要 动态切换算法,而不是写死在代码里。为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例。
设计框图:
这个很简单也不写了。
Template Method 模板模式
这个方法的思想是,给出做事情的步骤顺序,但是每一步有很多种实现方法。
设计框图:
其中TemplateAbstraction是一个抽象类,规定了方法有哪些步骤以及步骤的顺序。然后具体的步骤实现是在子类中。
例:
造一辆汽车的步骤顺序是一样的,但是每个步骤不同的汽车公司不一样。
设计如下:
代码:
抽象类:
public abstract class CarBuilder {
protected abstract void BuildSkeleton();
protected abstract void InstallEngine();
protected abstract void InstallDoor();
public void BuildCar() { //造车通用逻辑
BuildSkeleton();
InstallEngine();
InstallDoor();
}
}
子类:
public class PorcheBuilder extends CarBuilder {
protected void BuildSkeleton() {
System.out.println("Building Porche Skeleton");
protected void InstallEngine() {
System.out.println("Installing Porche Engine");
}
protected void InstallDoor() {
System.out.println("Installing Porche Door");
}
}
public class BeetleBuilder extends CarBuilder {
protected void BuildSkeleton() {
System.out.println("Building Beetle Skeleton");
}
protected void InstallEngine() {
System.out.println("Installing Beetle Engine");
}
protected void InstallDoor() {
System.out.println("Installing Beetle Door");
}
}
使用方法:
public static void main(String[] args) {
CarBuilder c = new PorcheBuilder();
c.BuildCar();
c = new BeetleBuilder();
c.BuildCar(); }
Iterator 迭代器模式
提供遍历容器中内容的方式。
设计框图:
先实现Iterable接口,再写一个具体的Iterator类
例子:
public class Pair<E> implements Iterable<E> {
private final E first, second;
public Pair(E f, E s) {
first = f; second = s;
}
public Iterator<E> iterator() {
return new PairIterator();
}
//内部类
private class PairIterator implements Iterator<E> {
private boolean seenFirst = false, seenSecond = false;
public boolean hasNext() {
return !seenSecond;
}
public E next() {
if (!seenFirst) {
seenFirst = true;
return first;
}
if (!seenSecond) {
seenSecond = true;
return second;
}
throw new NoSuchElementException();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
Observer 观察者模式
这是.一种“发布-订阅”形式,发布方的变化,会通知订阅方 。订阅方在发布方进行注册。这个模式可以类比粉丝和偶像,粉丝希望得知偶像的一举一动,于是粉丝就去偶像那里注册,偶像一旦有消息,就会推送给已经注册的粉丝(即回调粉丝的特定功能)。java提供了Observer接口,可以直接使用。
回调的目的大致可以分为:
- 推送通知
- 推送数据
- 拉取数据
设计框图:
例子:
public class Subject {//这里是偶像
private List<Observer> observers = new ArrayList<Observer>();//维护粉丝列表
private int state;//偶像的状态
public int getState() {return state;}//供粉丝和外界获取状态
public void setState(int state) {//设置状态
this.state = state;
notifyAllObservers();//状态更改之后,通知所有粉丝
}
public void attach(Observer observer){observers.add(observer);}//添加一个粉丝
//这里也可以有删除粉丝的功能
private void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
public abstract class Observer {
protected Subject subject;//这里保存了粉丝的偶像,这个属性可有可无,如果没有的话,粉丝就是完全被动的
public abstract void update();//供偶像回调的函数
}
public class BinaryObserver extends Observer{//一个具体的粉丝
public BinaryObserver(Subject subject){
this.subject = subject;//指定自己的偶像
this.subject.attach(this);
}
@Override
public void update() {//定制的行为
System.out.println( "Binary String: " +
Integer.toBinaryString(
subject.getState() ) );
}
}
public class ObserverPatternDemo {//客户端代码
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
Visitor 访问者模式
这个模式为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下在需要时委托机制通过接入ADT。这个模式使用了双向委托。
设计框图:
例子
对不同商品进行结算,不同的商品,计算总价的方式不同。
代码:
/* Abstract element interface (visitable) */
public interface ItemElement {//这是一个visitable的接口
public int accept(ShoppingCartVisitor visitor);
}
/* Concrete element */
public class Book implements ItemElement{
private double price;
...
int accept(ShoppingCartVisitor visitor) {//实现visit方法
visitor.visit(this);//把this传入
}
}
public class Fruit implements ItemElement{
private double weight;
...
int accept(ShoppingCartVisitor visitor) {//实现visit方法
visitor.visit(this);//把this传入
}
}
/* Abstract visitor interface */
public interface ShoppingCartVisitor {//visitor接口
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
public int visit(Book book) {//用来计算book的总价
int cost=0;
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else
cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
public int visit(Fruit fruit) {//用来计算fruit的总价
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
}
}
客户端代码:
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{
new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items)
sum = sum + item.accept(visitor);
return sum;
}
}
Factory Method 工厂方法模式
这个模式可以叫做虚拟构造器,使用户自定义的构造对象的方法。
结构框图:
这个模式很简单,就不举例了。要注意一点,最好把工厂类中的创建方法改成static,这样就不需要再多创建一个工厂对象。
Abstract Factory 抽象工厂模式
这个模式相当于工厂方法模式的升级版本,工厂方法模式只能产生特定的某个产品,而抽象工厂模式可以产生一个由多个产品的组合体。实质两者是一致的。
例子:
从上面可以看出,一个抽象工厂实际上是多个工厂的特定组合。
最后
以上就是稳重水池为你收集整理的设计模式(Design Pattern)设计模式(Design Pattern)的全部内容,希望文章能够帮你解决设计模式(Design Pattern)设计模式(Design Pattern)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复