码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式

时间:2017-05-06 10:24:38      阅读:257      评论:0      收藏:0      [点我收藏+]

标签:了解   导致   结束   cli   change   pretty   界面   load   实体   

更新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();
    }
}

实际应用场景

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

设计模式

标签:了解   导致   结束   cli   change   pretty   界面   load   实体   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!