首页 > 其他 > 详细

设计模式

时间:2017-05-06 09:53:41      阅读:361      评论:0      收藏:0      [点我收藏+]

更新ing

SOLID原则

缩写 英文 中文 描述
SRP The Single Responsibility Principle 单一责任原则 让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
OCP The Open Closed Principle 开放封闭原则 软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
LSP The Liskov Substitution Principle 里氏替换原则 当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系
DIP The Dependency Inversion Principle 依赖倒置原则 高层模块不应该依赖于低层模块,二者都应该依赖于抽象 2. 抽象不应该依赖于细节,细节应该依赖于抽象
ISP The Interface Segregation Principle 接口分离原则 不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好

设计模式

单例模式Singleton

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

扩展:可以创建固定数量的对象。

本质

控制实例数目

优点

  • 节约资源

缺点

  • 单例是一个虚拟机范围的,因为类装载功能是虚拟机的。对于集群环境,单例模式不适用。

示例

懒汉式:时间换空间

public class Singleton {

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton (){}

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

饿汉式:空间换时间

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance(){
        return instance;
    }
}

最佳方案

public enum Singleton {

    instance;

    public void operation() {
        // 操作
    }
}

实际应用场景

  • 读取配置文件
  • 缓存

抽象工厂Abstract Factory

定义

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

本质

选择产品簇的实现

优点

  • 分离接口和实现
  • 使得切换产品簇变得容易

缺点

  • 不太容易扩展新的产品
  • 容易造成类层次复杂

示例

技术分享

  • Abstract Factory:抽象工厂,定义创建一系列产品对象的操作接口
  • Concrete Factory:具体的工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建
  • Abstract Product:定义一类产品对象的接口
  • Concrete Product:具体的产品实现对象,通常在具体工厂里面,会选择具体的产品实现对象,来创建符合抽象工厂定义的方法返回的产品类型的对象
  • Client:客户端,主要使用抽象工厂来获取一系列所需要的产品对象,然后面向这些产品对象的接口编程,以实现需要的功能。

抽象工厂接口

public interface AbstractFactory {
    public AbstractProductA createProductA();
    public AbstractProductB createProductB();
}

抽象产品A的接口

public interface AbstractProductA {

}

抽象产品B的接口

public interface AbstractProductB {

}

产品A具体实现

public class ProductA1 implements AbstractProductA {
}

public class ProductA2 implements AbstractProductA {
}

产品B具体实现

public class ProductB1 implements AbstractProductB {
}

public class ProductB2 implements AbstractProductB {
}

具体工厂实现

public class ConcreteFactory1 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ProductA1();
    }

    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}

public class ConcreteFactory2 implements AbstractFactory {
    public AbstractProductA createProductA() {
        return new ProductA2();
    }

    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        AbstractFactory af = new ConcreteFactory1();
        af.createProductA();
        af.createProductB();
    }
}

实际应用场景

  • 电脑组装,有不同厂家的cpu和不同厂家的主板等一系列产品。
    技术分享

DAO(Data Access Object)数据访问对象

  • 数据源不同,例如:数据库数据源、LDAP数据源;本地数据源,远程数据源。
  • 存储类型不同,关系型数据库、面向对象数据库、纯文件、XML
  • 访问方式的不同,JDBC、JPA、EntityBean、Hibernate、iBatis
  • 供应商不同,oracel,mysql
  • 版本不同

DAO抽象和封装所有对数据的访问。

  • 底层存储方式固定可以用工厂方法
  • 底层存储方式不固定可以用抽象工厂模式

工厂方法Factory Method

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使用一个类的实例化延迟到其子类。

本质

延迟到子类来选择实现

优点

  • 让父类不知道具体实现的情况下,完成自身的功能调用;而具体的实现延迟到子类来实现
  • 更容易扩展对象的新版本,工厂方法给子类提供一个挂钩,使得扩展的对象版本变得非常容易
  • 连接平行的类层次

缺点

  • 具体产品对象和工厂方法的耦合性

示例

技术分享
技术分享

  • Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。
  • ConcreteProduct:具体的Product接口的实现对象
  • Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类型的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象。
  • ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例。

Product

public interface Product {
    // 定义Product的属性和方法
}

ConcreteProduct

public class ConcreteProduct implements Product {
    // 实现Product要求的方法
}

创建器

public abstract class Creator {

    // 创建Product的工厂方法,一般不对外
    protected abstract Product factoryMethod();

    // 提供给外部使用的方法
    public void someOperation() {
        Product product = factoryMethod();
    }
}

创建器具体实现

public class ConcreteCreator extends Creator {
    protected Product factoryMethod() {
        return new ConcreteProduct();
    }
}

实际应用场景

  • 实现一个导出数据的应用框架,来让客户选择数据的导出方式,并真正执行数据导出。各分公司在局域网独立运行自己的服务,每天业务结束将数据导出成某种格式,传送给总部,总部导入数据,核算。
    技术分享
  • 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类中去实现
  • 如果一个类本身就希望由它的子类来创建所需的对象的时候

IoC/DI

Q&A

  • 参与者都有谁?
    • 某个对象:任意的,普通的Java对象
    • Ioc/DI容器:框架程序
    • 某个对象的外部资源:对象需要的,但是从对象外部获取的,都统称为资源,比如对象需要的其他对象,或者是对象需要的文件资源等
  • 依赖:谁依赖于谁?为什么需要依赖?
    • 某个对象依赖于IoC/DI容器
    • 对象需要IoC/DI的容器来提供对象需要的外部资源
  • 注入:谁注入于谁?到底注入什么?
    • Ioc/DI容器注入某个对象
    • 注入某个对象所需要的外部资源
  • 控制反转:谁控制谁?控制什么?为何叫反转(有反转就应该有正转了)?
    • IoC/DI容器来控制对象
    • 主要控制对象实例的创建
    • 反转是相对正向而言的。如果A里面使用了C,当然会直接去创建C的对象,也就是说A主动去获取外部资源C,这叫正向。反向就是A不再主动去获取外部资源C,而是被动等待,等待Ioc/DI容器获取一个C的实例,然后反向注入到A类中。
  • 依赖注入和控制反转是同一概念吗?
    • 依赖注入和控制反转是对同一件事的不同描述。依赖注入(应用程序角度):应用程序依赖容器创建并注入它所需要的外部资源;控制反转(容器角度):容器控制应用程序,由容器反向地向应用程序注入其所需要的外部资源。

简单工厂Simple Factory

定义

提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口,抽象类也可以是具体类。

本质

选择实现

优点

  • 帮助封装,让组件外部能真正面向接口编程
  • 解耦,通过简单工厂,实现客户端和具体实现类解耦

缺点

  • 使用时,必须对配置非常熟悉
  • 静态方法创建接口,不方便扩展子工厂(通常也不需要扩展,所以不算一个严重的缺点)

示例

技术分享

  • Api:定义客户所需要的功能接口
  • Impl:具体实现Api的实现类,可能会有多个
  • Factory:工厂,选择合适的实现类来创建Api接口对象
  • Client:客户端,通过Factory来获取Api接口对象,然后面向Api接口编程

API定义

/**
 * 接口定义
 * /
public interface Api {
    public void operation(String s);
}

实现A

/**
 * 接口的具体实现A
 * /
public class ImplA implements Api {
    public void operation(String s) {
        System.out.println("ImplA s==" + s);
    }
}

实现B

/**
 * 接口的具体实现B
 * /
public class ImplB implements Api {
    public void operation(String s) {
        System.out.println("ImplB s==" + s);
    }
}

工厂类

public class Factory {
    public static Api createApi(int condition) {
        Api api = null;
        if(condition == 1) {
            api = new ImplA();
        } else if(condition == 2) {
            api = new ImplB();
        }
        return api;
    }
}

配置版工厂类(反射,可考虑用NIO优化)

public class FactoryTest {

    public static Api createApi() {
        Properties p = new Properties();
        InputStream in = null;
        try {
            in = FactoryTest.class.getResourceAsStream("FactoryTest.properties");
            p.load(in);
        } catch (IOException e) {
            System.out.println("");
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        Api api = null;
        try {
            api = (Api) Class.forName(p.getProperty("implClass")).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return api;
    }
}

客户端

public class Client {
    public static void main(String [] args) {
        Api api = Factory.createApi(1);
        api.operation("正在使用简单工厂");
    }
}

配置版客户端

public class Client {
    public static void main(String [] args) {
        Api api = Factory.createApi();
        api.operation("正在使用简单工厂");
    }
}

实际应用场景

  • 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体
  • 如果想要把创建对象的职责集中管理和控制

建造模式Builder

定义

将一个复杂的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

本质

分离整体构建算法和部件构造

优点

  • 松散耦合
  • 可以很容易地改变产品的内部表示。
  • 更好的复用性,很好的实现了构建算法和具体产品实现的分离。

缺点

未总结

示例

技术分享

  • Builder:生成器接口,定义创建一个Product对象所需的各个部件的操作。
  • ConcreteBuilder:具体的生成器实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供一个让用户获取组装完成后的产品对象的方法
  • Director:指导者,主要用来使用Builder接口,以一个统一的过程来构建所需要的Product对象
  • Product:产品,表示被生成器构建的复杂对象,包含多个部件

生成器接口

public interface Builder {
    public void buildPart();
}

具体生成器的实现

public class ConcreteBuilder implements Builder {
    // 生成器最终构建的产品对象
    private Product resultProduct;

    // 获取最终构建的产品对象
    public Product getResult() {
    }s

    public void buildPart() {
        // 构建某个部件
    }
}

相应的产品对象接口

public interface Product {

}

指导者

public class Director {
    // 持有当前需要使用的生成器对象
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    // 指导生成器构建最终的产品对象
    public void construct() {
        // 通过使用生成器接口来构建最终的产品对象
        builder.buildPart();
    }
}

实际应用场景

  • 一部分是部件构造和产品装配,另一部分是整体构建的算法
  • 如果创建对象的算法,应该独立于该对象的组成部分以及它们的装配方式时
  • 如果同一个构建过程有着不同的表示时

观察者模式Observer

定义

对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。

本质

触发联动

优点

  • 观察者模式实现了观察者和目标之间的抽象耦合
  • 观察者模式实现了动态联动
  • 观察者模式支持广播通信

缺点

  • 可能会引起无谓的操作

示例

  • Subject:目标对象,通常具有如下功能
    • 一个目标可以被多个观察者观察
    • 目标提供对观察者注册和退订的维护
    • 当目标的状态发生变化时,目标负责通知所有注册的,有效的观察者
  • Observer:定义观察者的接口,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据
  • ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生变化,通知所有注册的,有效的观察者,让观察者执行相应的处理。
  • ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身的状态以保持和目标的相应状态一致。

目标对象

public class Subject {
    private List<Observer> observers = new ArrayList<Observer>();

    // 注册观察者对象
    public void attach(Observer observer) {
        observers.add(observer);
    }

    // 删除观察者对象
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    // 通知所有注册的观察者对象
    protected void notifyObservers() {
        for(Observer observer: observers) {
            observer.update(this);
        }
    }
}

具体的目标对象

public class ConcreteSubject extends Subject {
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String sunjectState) {
        this.subjectState = subjectState;
        this.notifyObservers();
    }
}

观察者接口

public interface Observer {
    public void update(Subject subject);
}

具体观察者

public class ConcreteObserver implements Observer {
    private String observerState;

    public void update(Subject subject) {
        observerState = ((ConcreteSubject) subject).getSubjectState();
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        NewsPaper subject = new NewsPaper();
        Reader reader1 = new Reader();
        reader1.setName("张三");

        Reader reader2 = new Reader();
        reader2.setName("李四");

        Reader reader3 = new Reader();
        reader3.setName("李四");

        subject.attach(reader1);
        subject.attach(reader2);
        subject.attach(reader3);

        subject.setContent("本期内容是观察者模式");
    }
}

实际应用场景

  • 推模式:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的消息通常是目标对象的全部或部分数据,相当于是在广播通信。适用于目标对象知道观察者需要什么数据。
  • 拉模式:目标对象在通知观察者的时候,只传递少量信息。如果观察者需要具体的信息,由观察者主动到目标对象中获取,相当于是观察者从目标对象中拉数据,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。适用于目标对象不知道观察者需要什么数据,将自己的引用传给观察者,让观察者按需取值。这个更加通用。

中介者模式Mediator

定义

本质

封装隔离

优点

  • 松散耦合
  • 集中控制交互
  • 多对多变成一对多

缺点

  • 过度集中化。中介者对象十分复杂,难于管理和维护。

示例

  • Mediator:中介者接口,在里面定义各个同事之间交互需要的方法,可以是公共的通信方法,比如changed方法,大家都用,也可以是小范围的交互方法。
  • ConcreteMediator:具体中介者实现对象。它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
  • Colleague:同事类的定义,通常实现成抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能。
  • ConcreteColleague:具体的同事类,实现自己的业务,在需要与其他同事通信的时候,就与持有的中介者通信,中介者会负责与其他的同事交互。

同事类的抽象父类

public abstract class Colleague {

    private Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    public Mediator getMediator() {
        return mediator;
    }
}

同事类A

public class ConcreteColleagueA extends Colleague {
    public ConcreteColleagueA(Mediator mediator) {
        super(mediator);
    }

    public void someOperation() {
        // 需要跟其他同事通信的时候,通知中介者对象
        getMediator().changed(this);
    }
}

同事类B

public class ConcreteColleagueB extends Colleague {
    public ConcreteColleagueB(Mediator mediator) {
        super(mediator);
    }

    public void someOperation() {
        // 需要跟其他同事通信的时候,通知中介者对象
        getMediator().changed(this);
    }
}

中介者接口

public interface Mediator {

    public void changed(Colleague colleague);
}

中介者实现

public class ConcreateMediator implements Mediator {
    private ConcreteColleagueA colleagueA;

    private ConcreteColleagueB colleagueB;

    public void setConcreteColleagueA(ConcreteColleagueA colleague) {
        colleagueA = colleague;
    }

    public void setConcreteColleagueB(ConcreteColleagueB colleague) {
        colleagueB = colleague;
    }

    public void changed(Colleague colleague) {
        // 某个同事类发生了变化,通常需要与其他同事交互
        // 具体协调相应的同事对象来实现协作行为
    }
}

客户端

public class Client {

}

实际应用场景

  • 如果一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱,可以采用中介者模式
  • 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象

外观模式Facade

定义

为子系统中的一组接口提供一个一致的界面,也就是一个高层接口。

本质

为多模块提供统一接口,封装交互,简化调用

优点

  • 减少外部与子系统内多模块的交互,松散耦合
  • 出现变化时,只需修改Facade的实现就好,只需改这一个地方,由于很多客户端都会用这个外观模式,无需修改所有的客户端。从而达到一改全改。
  • 调用多个模块在外观模式调用一次就好了,无需所有客户端都调用一次,减少重复调用
  • 客户端无需了解系统的内部实现,无需了解每个模块的细节,也不需要去和多个模块交互,节省了学习成本。
  • 将外部接口和外部接口分开。封装更好

缺点

  • 过多或者不合理的Facade容易让人迷惑,到底是调用Facade好,还是直接调用模块好。

示例

技术分享
技术分享

A模块Api

public interface AModuleApi {
    public void testA1(); //系统外部使用
    public void testA2(); //系统内部使用
    public void testA3(); //系统内部使用
}

实现A模块

public class AModuleImpl implements AModuleApi {
    public void testA1() {
        System.out.println("现在在A模块里面操作testA1方法");
    }
    public void testA2() {
        System.out.println("现在在A模块里面操作testA2方法");
    }
    public void testA3() {
        System.out.println("现在在A模块里面操作testA3方法");
    }
}

B模块Api

public interface BModuleApi {
    public void testB1(); //系统外部使用
    public void testB2(); //系统内部使用
    public void testB3(); //系统内部使用
}

实现B模块

public class BModuleImpl implements BModuleApi {
    public void testB1() {
        System.out.println("现在在B模块里面操作testB1方法");
    }
    public void testB2() {
        System.out.println("现在在B模块里面操作testB2方法");
    }
    public void testB3() {
        System.out.println("现在在B模块里面操作testB3方法");
    }
}

C模块Api

public interface CModuleApi {
    public void testC1(); //系统外部使用
    public void testC2(); //系统内部使用
    public void testC3(); //系统内部使用
}

实现B模块

public class CModuleImpl implements CModuleApi {
    public void testC1() {
        System.out.println("现在在C模块里面操作testC1方法");
    }
    public void testC2() {
        System.out.println("现在在C模块里面操作testC2方法");
    }
    public void testC3() {
        System.out.println("现在在C模块里面操作testC3方法");
    }
}

外观接口

public interface FacadeApi {
    // 只暴露对外部调用的接口
    public void testA1();
    public void testB1();
    public void testC1();
    public void test();
}

外观类

public class Facade {
    public void test() {
        AModuleApi a = new AModuleImpl();
        a.testA1();
        BModuleApi b = new BModuleApi();
        b.testB1();
        CModuleApi c = new CModuleImpl();
        c.testC1();
    }
}

客户端

public class Client {
    public static void main(String [] args) {
        Facade facade = new Facade();
        facade.test();
    }
}

实际应用场景

适配器模式Adapter

定义

把不兼容的接口转换匹配成客户需要的接口,复用已有的功能

本质

转换匹配,复用功能。

优点

  • 兼容老接口,更好的复用。
  • 更好的扩展

缺点

  • 过多使用适配器,会让系统非常零乱,不容易整体进行把握。

示例

技术分享
技术分享

  • Client:客户端,调用自己需要的领域接口Target
  • Traget:定义客户端需要的跟特定领域相关的接口
  • Adaptee:已经存在的接口,通常能满足客户端的功能要求,但是接口与客户端要求的特定领域接口不一致,需要被适配
  • Adapter:适配器,把Adaptee适配成为Client需要的Target

Target Api

public interface Target {
    public void request();
}

被适配的类

public class Adaptee {
    public void specificRequest() {
        //具体功能
    }
}

适配器

public class Adapter implements Target {
    private Adaptee adaptee;
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    public void request() {
    }
}

客户端

public class Client {
    public static void main(String [] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

实际应用场景

  • 如果想要使用一个已经存在的类,但是它的接口不符合你的需求
  • 如果想创建一个可以复用的类,这个类可能和一些不兼容的类一起工作
  • 如果想使用一些已经存在的子类,但是不可能对每一个子类都进行适配,这种情况可以选用对象适配器,直接适配这些子类的父类就可以了

设计模式

原文:http://blog.csdn.net/echo_follow_heart/article/details/71075681

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!