搜索
您的当前位置:首页正文

常用设计模式「Design Pattern」示例

来源:二三娱乐
Mosaic

设计模式「Design Pattern」可谓软件设计思想精髓的集中体现,是前人在大量实践中的总结和提炼,网络上相关的资料多如牛毛,好像没有炒冷饭的必要,但这不代表自己能够吸收并且在工程项目中灵活运用,还是自己总结来的比较实在。

在总结常用的设计模式之前,有必要对设计模式的几条基本原则进行充分的理解:

开闭原则(Open Close Principle)

简单说就是「对修改关闭,对扩展开放」,怎么理解:在设计项目的一个模块(比如根据账户类别查询账户信息)时,假设我们已经实现根据「往来户」和「内部户」这两种账户类别查询账户信息,后续当我们新增账户类别时,我们并不希望去修改原有的业务逻辑,而是针对新增的账户类别查询子模块实现热插拔的效果。我们往往需要借助接口和抽象类来实现这样的效果。

里氏代换原则(Liskov Substitution Principle)

里式代换原则是面向对象语言设计的基本原则之一,是软件功能单元复用的基础。简单的说,该原则表明了:「所有基类可以出现的地方,子类一定可以出现」。是对抽象的「开闭原则」的具体实现,在之后模式的总结中,会大量看到里式替换原则的运用。

依赖倒转原则(Dependence Inversion Principle)

该原则更像是一种方法论,核心内容是:针对接口编程,依赖于抽象的接口而不是具体的实现。

接口隔离原则(Interface Segregation Principle)

该原则提倡我们使用多个接口,而不是单个,以便于接口与接口之间的隔离,降低接口之间的耦合度。

迪米特法则(最少知道原则)(Demeter Principle)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

合成复用原则(Composite Reuse Principle)

尽量使用合成/聚合的方式,而不是继承。

在介绍完设计模式的六条基本原则之后,下面来具体说说常用的设计模式,被前人归纳总结出来的设计模式大约有23种(最著名的就是那部惟妙惟肖的GoF巨头了),可以分成以下三类:

  • 创建型模式(共五种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式;
  • 结构型模式(共七种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式;
  • 行为型模式(共十一种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式;

模式何其多,想必在实际工程项目中能同时使用这些模式的可能性真的很小了吧,实践的少,往往导致半瓶子醋的下场,所以还是对比较常用的模式进行总结:

[1]抽象工厂模式

示例代码:

// 公共接口
public interface Sender {  
    public void Send();  
}
  
// 三个实现类:
public class MailSender implements Sender {  
    @Override  
    public void Send() {  
        System.out.println("this is mailsender!");  
    }  
}  
public class SmsSender implements Sender {  
      @Override  
    public void Send() {  
        System.out.println("this is sms sender!");  
    }  
}
public class VoiceSender implements Sender {  
      @Override  
    public void Send() {  
        System.out.println("this is voice sender!");  
    }  
}
// 还可以扩展许多具体的sender类型
  
// 抽象工厂的公共接口:
public interface Provider {  
    public Sender produce();  
}  

// 三个具体工厂类,对每个具体类型的sender提供工厂方法
public class SendMailFactory implements Provider {  
    @Override  
    public Sender produce(){  
        return new MailSender();  
    }  
}  
public class SendSmsFactory implements Provider{  
    @Override  
    public Sender produce() {  
        return new SmsSender();  
    }  
} 
public class SendVoiceFactory implements Provider{  
    @Override  
    public Sender produce() {  
        return new VoiceSender();  
    }  
}  

// 测试类
public class Test {  
     public static void main(String[] args) {  
        Provider provider = new SendVoiceFactory();  
        Sender sender = provider.produce();  
        sender.Send();  
    }  
}

使用抽象工厂模式的好处显而易见,每当新增一种具体类型的sender时,我们只需要实现Sender接口,同时提供配套的工厂方法,而无需修改原有的业务逻辑,方便扩展。

[2]单例模式

单列模式在实际的工程项目中使用更加频繁,之所以运用比较多,总结起来有这样显而易见的好处:减少内存开销和GC压力,因为单例对象在一个JVM中仅且存在一个,省去很多次new操作带来的系统开销,示例如下:

public class SingletonTest {  
     // 声明单例对象
     private static SingletonTest instance = null;  
     
     private SingletonTest() {}  
     
     private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }  
     }  
     public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
     } 
}

[3]适配器模式

转换某个类的接口,成为客户端期望的另一种接口表示,消除由于接口不适配引起的类兼容性问题。主要分成三类:类的适配器模式;对象的适配器模式和接口的适配器模式。
类的适配器模式示例如下:

// 待匹配接口method1所在的类Source
public class Source {  
     public void method1() {  
        System.out.println("this is original method!");  
    }  
} 

// 目标接口所在的类Targetable 
public interface Targetable {
    public void method1();  // 同类Source中的方法
    public void method2();  // 新增方法
}  

// 适配器类,继承Source类,实现Targetable接口
public class Adapter extends Source implements Targetable {  
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }  
} 
// 测试类
public class AdapterTest {  
     public static void main(String[] args) {  
        Targetable target = new Adapter();  
        target.method1();  
        target.method2();  
    }  
}

以上就完成了把类Source中的接口适配到Targetable类中的目的,下面再看对象的适配器模式,其实非常简单就是在适配器类中加入对适配原对象的引用,示例如下:

// 待匹配接口method1所在的类Source
public class Source {  
     public void method1() {  
        System.out.println("this is original method!");  
    }  
} 

// 目标接口所在的类Targetable 
public interface Targetable {
    public void method1();  // 同类Source中的方法
    public void method2();  // 新增方法
}  

// 适配器类,实现Targetable接口,加入对Source类的引用
public class Adapter implements Targetable {  
    private Source source;
    public Adapter(Source source){
        super();
        this.source  = source;
    }
    @Override
    public void method1(){
        source.method1(); // 调用Source类中的method1方法
    }
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }  
} 
 
//测试类:
public class AdapterTest {  
     public static void main(String[] args) { 
        Source source = new Source()
        Targetable targetable = new Adapter(source);
        targetable.method1();  
        targetable.method2();  
    }  
}

说明了对象的适配器模式,接着说说接口的适配器模式,有时我们定义一个接口,接口中包含了很多方法的声明,任何继承该接口的实现类必须都要实现接口中事先声明的方法,但真实环境下我们可能只是需要这个接口中的一个或者两个,不需要全部,这个时候我们就可以实现一个抽象类,该抽象类实现接口中声明的所有方法,其他类继承该抽象类,根据需要重写相应的方法即可,示例如下:

// 公共接口
public interface Sourceable {  
    public void method1();  
    public void method2();  
}  

// 抽象类,实现公共接口中的方法
public abstract class Wrapper implements Sourceable{  
    public void method1(){}  
    public void method2(){}  
} 

//  子类1,只重写method1
public class SourceSub1 extends Wrapper {  
    public void method1(){  
        System.out.println("the sourceable interface's first Sub1!");  
    }  
}  

// 子类2,重写method2
public class SourceSub2 extends Wrapper {  
    public void method2(){  
        System.out.println("the sourceable interface's second Sub2!");  
    }  
}  

// 测试类
public class WrapperTest {  
     public static void main(String[] args) {  
        Sourceable source1 = new SourceSub1();  
        Sourceable source2 = new SourceSub2();  
        source1.method1();  
        source1.method2();  
        source2.method1();  
        source2.method2();  
    }  
}

[4]代理模式

代理模式理解起来相对比较简单,就是在调用者和被调用者之间,增加一个代理者的角色,调用者通过代理者调用目标方法,示例代码如下:

// 公用接口
public interface Sourceable {  
    public void method();  
} 

// 被调用者 
public class Source implements Sourceable {  
     @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
} 

// 代理者 
public class Proxy implements Sourceable {  
    // 对被调用者的引用
    private Source source;  
    public Proxy(){  
        super();  
        this.source = new Source();  
    }  
    @Override  
    public void method() {  
        before();  
        source.method();  // 调用被调用者的方法
        atfer();  
    }  
    private void atfer() {  
        System.out.println("after proxy!");  
    }  
    private void before() {  
        System.out.println("before proxy!");  
    }  
}  

// 测试类
public class ProxyTest {  
     public static void main(String[] args) {  
        Sourceable source = new Proxy();  
        source.method();  
    }  
}

[5]观察者模式

观察者模式非常好理解,它表达的是类与类之间的关系,不涉及到继承,类似于RSS订阅。比如我们经常浏览一些技术博客,在这些站点上基本能看到RSS订阅源的图标,假设我们订阅了某个博客或者站点的RSS源,那么这些站点的内容一旦有更新,作为订阅者(观察者)的我们就马上能得到通知。示例代码如下:

/* 公用的观察者接口,包含一个内容更新通知的方法,
   订阅源的内容发生更新时,可以调用update方法,把新内容通知给观察者 */
public interface Observer {  
    public void update();  
}  

// 具体的观察者1
public class Observer1 implements Observer {  
    @Override  
    public void update() {  
        System.out.println("observer1 has received!");  
    }  
}  

// 具体的观察者2
public class Observer2 implements Observer {  
    @Override  
    public void update() {  
        System.out.println("observer2 has received!");  
    }  
}  

// 订阅源主体的公用接口
public interface Subject { 
    public void add(Observer observer);  // 增加观察者  
    public void del(Observer observer);   // 删除观察者 
    public void notifyObservers();    // 通知所有观察者
    public void operation();  
}  

// 实现公用接口所有方法的抽象类
public abstract class AbstractSubject implements Subject {  
    private Vector<Observer> vector = new Vector<Observer>();  
    
    @Override  
    public void add(Observer observer) {  
        vector.add(observer);  
    }  
    
    @Override  
    public void del(Observer observer) {  
        vector.remove(observer);  
    }  
    
    @Override  
    public void notifyObservers() {  
        Enumeration<Observer> enumo = vector.elements();  
        while(enumo.hasMoreElements()){  
            enumo.nextElement().update();  
        }  
    }  
} 

// 某个具体的订阅源 
public class MySubject extends AbstractSubject {  
  
    @Override  
    public void operation() {  
        System.out.println("update self!");  
        notifyObservers();  
    }  
 }  

// 测试类
public class ObserverTest {  
     public static void main(String[] args) {  
        Subject sub = new MySubject();  
        sub.add(new Observer1());  
        sub.add(new Observer2());  
        sub.operation();  
    }  
 }

结束语

以上总结了几种比较常用的设计模式,当然还有其他常用的设计模式未能涉及,留待后续实践和总结

Top