zoukankan      html  css  js  c++  java
  • 技能篇:实际开发常用设计模式

    创建型

    单例模式

    单例对象能节约系统资源,一个对象的创建和消亡的开销可能很小。但是日常的服务接口,就算是一般小公司也有十几万的QPS吧。每一次的功能运转都创建新的对象来响应请求,十几万对象的创建和销毁,想想就是一笔大开销,所以 spring 管理构造的 bean 对象一般都是单例。而且单例模式可以更好的解决并发的问题,方便实现数据的同步性

    • 优点
      • 在内存中只有一个对象,节省内存空间
      • 避免频繁的创建销毁对象,可以提高性能
      • 避免对共享资源的多重占用,简化访问
      • 为整个系统提供一个全局访问点
    • 缺点
      • 不适用于变化频繁的对象
    //饿汉式
    private static Singleton singleton = new Singleton();
    
    //懒汉式
    private static Singleton singleton;
    public static Singleton getSingleton(){
        if (singleton == null) {
            singleton = new Singleton(); //被动创建,在真正需要使用时才去创建
        }
        return singleton;
    }
    
    //双重判断加锁机制
    private volatile static Singleton instance;
    //程序运行时创建一个静态只读的进程辅助对象
    public static Singleton GetInstance() {
        //先判断是否存在,不存在再加锁处理
        if (instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
    
    //静态初始化
    private static readonly Singleton instance= new Singleton();
    public static Singleton GetInstance(){
        return instance;
    }
    

    工厂模式

    使用者不关心对象的实例化过程,只关心对象的获取。工厂模式使得产品的实例化过程和消费者解耦

    • 优点
      • 一个调用者想创建一个对象,只需通过其名称或其他唯一键值在工厂获取
      • 扩展性高,如果想增加生产一种类型对象,只要扩展工厂类就可以
    • 缺点
      • 工厂类不太理想,因为每增加一产品,都要在工厂类中增加相应的生产判断逻辑,这是违背开闭原则的
    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 SendFactory {  
        public Sender produce(String type) {  
            if ("mail".equals(type)) {  
                return new MailSender();  
            } else if ("sms".equals(type)) {  
                return new SmsSender();  
            } else {  
                return null;  
            }  
        } 
        //若还有其他产品 则在工厂里加对应的 produce 方法
    }  
    

    建造者模式

    主要解决在软件系统中一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定

    • 优点
      • 扩展性好,对象每一个属性的构建相互独立,有利于解耦。
      • 建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险
    • 缺点
      • 如果对象建造者发生变化,则建造者也要同步修改,后期维护成本较大
      • 一种建造者对应一种类型建造,一个建造者基本很难建造多种类型对象
    @Data
    class Product {
        private String name;
        private String price;
        // Product 的建造者  Builder
        public static class Builder{
            public static Builder builder(){
                Builder builder = Builder();
            }
            private Product product = new Product();
            public Builder name(String name){ product.name = name; return this;}
            public Builder price(String price){ product.price = price; return this; }
            //返回产品对象
            public Product build() { return product; }
        }
    }
    

    结构型

    适配器模式

    连通上下游功能。一般是现有的功能和产品要求的接口不兼容,需要做转换适配。平时见到的 PO,BO,VO,DTO 模型对象之间的相互转换也是一种适配的过程

    • 优点:提高了类的复用,灵活性好
    • 缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现
    //类的适配器模式
    public class Source {  
        public void sayHello() {  
            System.out.println("lwl:hello!");  
        }  
    }  
    public interface Targetable {  
        /* Source方法相同 */  
        public void sayHello();  
        /* 新增的方法 */  
        public void hi();  
    }
    // Source 用 Adapter 适配成 Targetable
    public class Adapter extends Source implements Targetable {  
        @Override  
        public void hi() {  
            System.out.println("csc:hi!");  
        }  
    }
    
    //对象的适配器模式
    public class Source {  
        public void sayHello() {  
            System.out.println("lwl:hello!");  
        }  
    }  
    public interface Targetable {  
        /* Source方法相同 */  
        public void sayHello();  
        /* 新增的方法 */  
        public void hi();  
    }
    //  Source的对象适配成 Targetable
    public class Adapter implements Targetable {
        private Source source;  
        public Adapter(Source source){ this.source = source; }
        public void sayHello(){ source.sayHello(); }
        @Override  
        public void hi() {  
            System.out.println("csc:hi!");  
        }  
    }
    

    装饰器模式

    增强对象功能,动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删)

    public interface Show(){ public void acting(); }
    public class Artist implements Show {  
        public void acting(){
            System.out.println("lwl 在唱歌!");  
        }
    }  
    public class DecoratorArtist implements Show{
        Artist artist;
        DecoratorArt(Artist artist){
            this.artist = artist;
        }
        public void acting(){
            System.out.println("lwl 在弹钢琴!"); //增强的功能
            this.artist.acting(); 
            System.out.println("表演完毕!"); //增强的功能
        }
    }
    

    代理模式

    代理类是客户类和委托类的中介,可以通过给代理类增加额外的功能来扩展委托类的功能,这样只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则

    • 和装饰器模式的区别:代理模式着重于增强类功能,且对面屏蔽原对象的创建过程;装饰器模式增强的是对象,且装饰器模式有一个动态传递原对象的步骤
    • 和对象的适配器模式优点像:不过代理模式着重的是对原功能增强,适配器模式着重的是对新功能的兼容
    • 优点-1、职责清晰。 2、高扩展性
    public class Artist implements Show {  
        public void acting(){
            System.out.println("lwl 在唱歌!");  
        }
    }
    public class ProxyArtist implements Show{
        Artist artist;
        ProxyArtist(){ 
            this.artist = new Artist();//屏蔽了 artist 对象的创建
        }
        public void acting(){
            System.out.println("lwl 在弹钢琴!"); //增强的功能
            this.artist.acting(); 
            System.out.println("表演完毕!"); //增强的功能
        }
    }
    public class Demo {
        public static void main(String[] arg){
            Show show = new ProxyArtist();
            show.acting();
        }
    }
    

    桥接模式

    桥接模式侧重于功能的抽象,从而基于这些抽象接口构建上层功能。一般的java 项目都会将接口和实现分离原因,就是基于桥接模式。提高了系统的扩展能力,当引用的底层逻辑有不同的设计实现时,继承抽象接口重新实现一套即可,旧的不变,符合代码设计的开闭原则

    • jdbc 的驱动:常用的JDBC 和 DriverManager,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接
    • Unix 的文件系统:VFS(virtual File System)使得 Unix 系统可以在不同物理介质上的不同文件系统进行读写
    public interface FileSystem(){ 
      public void open(int file); 
      public String loading(int file); 
      public void store(int file, String data); 
    }
    //网络上的文件系统
    public class NetFileSystem implements FileSystem {  
      public void open(int file){ System.out.println(" netfile opening...."); }
      public String loading(int file) {System.out.println(" net loading ...."); }  
      public void store(int file, String data) {System.out.println(" send to network ...."); }  
    }
    //磁盘文件系统
    public class DiskFileSystem implements FileSystem{
      public void open(int file){ System.out.println(" disk opening...."); }
      public String loading(int file) {System.out.println(" disk loading ...."); } 
      public void store(int file, String data) {System.out.println(" write back disk ...."); }   
    }
    public class Linux {
        FileSystem fileSystem; 
        //底层功能提供接口,桥接模式:功能和具体实现分离
        //可以桥接 NetFileSystem 或者 DiskFileSystem 作为文件系统
        public void set(FileSystem fileSystem){ this.fileSystem = fileSystem; }
        //上层功能读数据
        public String read(int file){
            fileSystem.open(file);
            ... // Linux 自己的系统功能
            fileSystem.loading(file);
            ... 
        }
        //上层功能写数据
        public String write(int file, String data){
            fileSystem.open(file);
            ....
            fileSystem.store(file,data);
        }
    }
    
    • 可配合适配器模式使用

    享元模式

    多个对象共享某些属性。在创建有大量对象时,可能会造成内存溢出,把其中共同的部分抽象出来,如果有相同的请求,直接返回在内存中同一份属性,避免重新创建

    • 如 jdbc 连接池的连接对象,它们会共享池对象的 url、driverClassName、username、password 等属性
    public class ConnectionPool {  
        private Vector<Connection> pool;  
        /*公有属性*/  
        private String url = "jdbc:mysql://localhost:3306/test";  
        private String username = "root";  
        private String password = "root";  
        private String driverClassName = "com.mysql.jdbc.Driver";  
        public ConnectionPool() {  
            pool = new Vector<Connection>(poolSize);  
            for (int i = 0; i < poolSize; i++) {  
                Class.forName(driverClassName);  
                    // 每一个 conn 共享了 driverClassName ,url, username, password 等属性
                    Connection conn = DriverManager.getConnection(url, username, password);  
                    pool.add(conn);
                }
        }
        ....
    }
    

    外观模式

    • 用多个不同的对象实现一组更复杂的功能。使得类与类之间的关系解耦。如 spring 将使用各个简单的 component、dao 实现复杂的service,就是一种外观模式
    • 功能的组合,组合优于继承
    public class DAO { 
        public void queryData(){
            System.out.print(" query data ")
        }
    }
    public class Deal {  
        public void dealData(){
            System.out.print(" dealing data ")
        }
    }
    public class Sender {  
        public void send(){
            System.out.print(" send data ")
        }
    }
    public class Service(){
        private DAO dao;  
        private Deal deal;  
        private Sender sender;
        //封装 DAO,Deal,Sender 的功能,统一对外提供服务
        public void reponse(){
            dao.queryData();
            deal.dealData();
            sender.send();
        }
    }
    

    行为型

    策略模式

    策略模式侧重于不同的场景使用不同的策略。在有多种算法相似的情况下,解决 if...else 所带来的复杂和难以维护

    • 和桥接模式的区别:而桥接模式是结构型模式,侧重于分离底层功能的抽象和实现,底层只有一种实现也可以
    // 上学的策略
    abstract class Strategy{
        private static final Map<Integer,Strategy> strategyMap = new ConcurrentHashMap<>();
        public Strategy(){
            strategyMap.put(getType(), this);
        }
        public static Strategy routing(int type){
            return strategyMap.get(type);
        }
        abstract int getType();
        abstract void method(); //留待子类实现差异
    }
    //跑路去学校
    class RunningStrategy extends Strategy{
        int getType() { return 0; }
        void method() { System.out.println(" Run to school "); }
    }
    //公交去学校
    class BusStrategy extends Strategy{
        int getType() { return 1; }
        void method() { System.out.println(" Go to school by bus "); }
    }
    //飞去学校
    class FlyStrategy extends Strategy{
        int getType() { return 2; }
        void method() { System.out.println(" Fly to school "); }
    }
    class Context{
        //使用不同的策略
        void method(int strategy){
            Strategy.routing(strategy).method();
        }
    }
    

    模板方法

    和享元模式有一定的相似处,享元模式侧重于属性的共享,而且是结构上的引用,不一定需要继承;而模板方法是共享相同行为,一定有继承行为

    • 区别于策略模式是它有能抽象出来的共同行为,每一个子类再实现有差异细节
    abstract class AbstractHandler{
        // handle是抽象出来的共同逻辑
        void handle(String data){
            System.out.println("通用逻辑1...");
            stepOne(data);
            System.out.println("通用逻辑2...");
            stepTwo(data);
            System.out.println("通用逻辑3...");
        }
        abstract void stepOne(String data); //留待子类实现差异
        abstract void stepTwo(String data); //留待子类实现差异
    }
    class HelloHandler extends AbstractHandler{
        @Override
        void stepOne(String data) {
            System.out.println("hello: "+data);
        }
        @Override
        void stepTwo(String data) {
            System.out.println("hi: "+data);
        }
    }
    

    迭代子模式

    循环处理多个相同对象,用来遍历集合或者数组

    //迭代的抽象接口
    public interface Iterator {  
        //前移  
        public Object previous();  
        //后移  
        public Object next();  
        public boolean hasNext();   
    } 
    // 数组的迭代类
    public class ArrayIterator implements Iterator {  
        private Object[] datas; 
        private int cur = 0;  
        public ArrayIterator(Object[] datas){  
            this.datas = datas;  
        }  
        public String previous() {  
            if(cur > 0){ cur--;}  
            return datas[cur];  
        }  
        public Object next() {  
            if(cur < datas.length-1){ cur++;}  
            return datas[cur];  
        }  
        public boolean hasNext() {  
            return pos < datas.length-1 ? true :false;
        }  
    }  
    

    责任链模式

    负责处理上游的传递下来的对象,并传递给下一个处理者

    • 和迭代子模式的区别,责任链模式是多个hander处理同一个data,且 hander 处理具有顺序性,不用全部 hander 处理,可在某一 hander 中断,也可继续传递
    abstract class Handler<T,R> {
        private Handler<R,?> next;
        abstract R handle(T data);
        public void setNext(Handler<R, ?> next){ this.next = next; }
        public void loopHandle(T data){
            R result = this.handle(data);
            if(next!=null && result!=null ) { next.loopHandle(result); }
        }
    }
    //负责问候
    class HelloHandler extends Handler<String, Integer> {
        Integer handle(String data) {
            System.out.println(data + " hello! ");
            return 10;
        }
    }
    //负责计数
    class CountHandler extends Handler<Integer, Double> {
        Double handle(Integer data) {
            System.out.println(" it is " + data);
            return 2.0;
        }
    }
    public class demo{
        public static void main(String[] args){
            HelloHandler hello = new HelloHandler();
            CountHandler count = new CountHandler();
            hello.setNext(count);
            hello.loopHandle("lwl");
        }
    }
    

    观察者模式

    事件通知: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知

    • 优点:观察者和被观察者是抽象耦合的
    • 缺点
      • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
      • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
    //观察者
    public abstract class Observer<T> {
        public abstract void update(T data);
    }
    // 被观察对象
    public class Subject<T> {
        private List<Observer<T>> observers = new ArrayList<>();
        private T state;
        public void deal() { 
            ....// 逻辑处理
            //如果修改了 state,通知观察者
            if(...) notifyAllObservers();
        }
        //增加一个观察观察
        public void observe(Observer<T> observer) {
            observers.add(observer);
        }
        public void notifyAllObservers() {
            for (Observer<T> observer : observers) {
                observer.update(state);
            }
        }
    }
    

    状态机模式

    不同的状态不同的响应,实现状态之间的转移

    • 和策略模式的区别
      • 状态机模式是策略模式的孪生兄弟。策略模式可以让用户指定更换的策略算法,而状态机模式是状态在满足一定条件下的自动更换,用户无法指定状态,最多只能设置初始状态
      • 状态机模式重点在各状态之间的切换,从而做不同的事情;而策略模式更侧重于根据具体情况选择策略,并不涉及切换
    interface State<T> {
        //当前状态进行处理数据,并返回下一个状态
        abstract State<T> action(T data);
    }
    @Data
    class Context<T>{
        private State<T> state;
        public void invoke(T data){
            state != null ? state = state.action(data) : System.out.println(" nothing " + data);
        }
    }
    // HelloState -> HiState
    class HelloState implements State<String>{
        public State<String> action(String data) {
            System.out.println("hello!" + data);
            return new HiState();
        }
    }
    // HiState -> FineState
    class HiState implements State<String>{
        public State<String> action(String data) {
            System.out.println("how are you ?" + data);
            return new FineState();
        }
    }
    //最后的状态
    class FineState implements State<String>{
        public State<String> action(String data) {
            System.out.println("I am  fine!");
            return null;
        }
    }
    public class demo{
        public static void main(String[] args){
            Context<String> context = new Context<>();
            context.setState(new HelloState());
            context.invoke("lwl");
            context.invoke("lwl");
            context.invoke("lwl");
            context.invoke("lwl");
        }
    }
    

    备忘录

    记录上一次的状态,方便回滚。很多时候我们是需要记录当前的状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,恢复到原先的状态

    • 缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
    @Data
    public class Memento { 
        private String state;
        public Memento(String state){ this.state = state; }
    }
    @Data
    public class Storage { 
        private String value;  
        public void storeMemento(){
            return new Memento(value);  
        }
        public void restoreMemento(Memento memento){  
            this.value = memento.getValue();  
        }
        public void action(){ System.out.println(" Storage类逻辑运行 ");}
          
    }
    public class MementoPatternDemo { 
        public static void main(String[] args) {
            Storage storage = new Storage();
            storage.setValue(1);
            storage.storeMemento();//备忘,一下
            storage.action();//....逻辑运行
            restoreMemento(Memento memento);//使用备忘录的恢复状态
        }
    }
    
  • 相关阅读:
    (十)条件判断
    (九)字符处理命令
    (八)awk命令
    (六)环境变量配置文件
    (七)grep命令行提取符号
    Ⅶ 类模板与STL编程 ②
    Ⅵ 虚函数与多态性
    Ⅴ 运算符重载
    Ⅳ 继承与派生②
    Ⅳ 继承与派生①
  • 原文地址:https://www.cnblogs.com/cscw/p/15498485.html
Copyright © 2011-2022 走看看