用最简单的例子说明设计模式(一)之单例模式、工厂模式、装饰模式、外观模式

设计模式

  • 所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。 
  • 在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(创建型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一起形成更大的结构]、行为型[对在不同的对象之间划分责任和算法的抽象化])共23种设计模式,包括:Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。 

哪些源码用到了设计模式

责任链模式:try-catch、有序广播、viewgroup事件传递

建造者模式:AlertDialog

装饰模式:Collections工具类、I/O、context

观察者模:android中的回调模式、listview的notifyDataChanged、rxjava

外观模式:context

模板方法模式:AsnycTask、Activity

策略:Volley

单例

保证一个类在内存中的对象唯一性。

1,不允许其他程序用new创建该类对象。

2,在该类创建一个本类实例。

3,对外提供一个方法让其他程序可以获取该对象。

好处:

对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销

由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间

饿汉

public class HungurySingleton {
    private static final HungurySingleton mHungurySingleton = new HungurySingleton();
    private HungurySingleton(){
        System.out.println("Singleton is create");
        
    }
    public static HungurySingleton getHungurySingleton() {
        return mHungurySingleton;
    }
    
    public static void createString(){
         System.out.println("createString in Singleton");
    }
          
    public static void main(String[] args){
        HungurySingleton.createString();
    }
}

懒汉

public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        // 第一次调用的时候会被初始化
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
    
    public static void createString(){
         System.out.println("create String");
    }
          
    public static void main(String[] args){
         LazySingleton.createString();
    }
}

懒汉安全

public class LazySafetySingleton {
    private static LazySafetySingleton instance;  
    private LazySafetySingleton (){
    }
    
    public static void createString(){
     System.out.println("create String");
    }
      
   public static void main(String[] args){
     LazySingleton.createString();
   }
   
   //方法中声明synchronized关键字
   public static synchronized LazySafetySingleton getInstance() {
     if (instance == null) {  
         instance = new LazySafetySingleton();  
     }  
     return instance;  
   }
   
   //同步代码块实现
   public static LazySafetySingleton getInstance1() {  
           synchronized (LazySafetySingleton.class) {  
               if(instance == null){//懒汉式   
                   instance = new LazySafetySingleton();  
               }  
           }  
       return instance;  
   }  
}

DCL

假设没有关键字volatile的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行instance = new Instance(),该构造方法是一个非原子操作,编译后生成多条字节码指令,由于JAVA的指令重排序,可能会先执行instance的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后instance便不为空了,但是实际的初始化操作却还没有执行,如果就在此时线程B进入,就会看到一个不为空的但是不完整(没有完成初始化)的Instance对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。

加入同步为了解决多线程安全问题。

加入双重判断是为了解决效率问题。 

不写if语句的话,每次都会判断同步锁,这样写的话只在第一次判断并创建对象。以后在不会执行if里的代码了

public class DclSingleton {
    private static volatile DclSingleton mInstance = null;
//  private static DclSingleton mInstance = null;
    private DclSingleton() {
    }
    public void doSomething() {
        System.out.println("do sth.");
    }
    public static DclSingleton getInstance() {
        // 避免不必要的同步
        if (mInstance == null) {
            // 同步
            synchronized (DclSingleton.class) {
                // 在第一次调用时初始化
                if (mInstance == null) {
                    mInstance = new DclSingleton();
                }
            }
        }
        return mInstance;
    }
}

静态内部类单例(推荐)

静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝,和单例的效果一样。

final表示变量只能一次赋值以后值不能被修改(常量),保证了安全性。

并且由于私有的属性,他人无法使用SingleHolder,不调用Singleton.getInstance()就不会创建实例。

public class StaticFactorySingleton {
    //initailzed during class loading
    private static final StaticFactorySingleton INSTANCE = new StaticFactorySingleton();
 
    //to prevent creating another instance of Singleton
    private StaticFactorySingleton(){}
 
    public static StaticFactorySingleton getSingleton(){
        return INSTANCE;
    }
}

枚举单例(推荐)

public enum EnumSingleton {
     //定义一个枚举的元素,它就是 Singleton 的一个实例
    INSTANCE;  
    
    public void doSomeThing() {  
         // do something...
    }  
}

 总结

饿汉:无法对instance实例进行延迟加载

懒汉:多线程并发情况下无法保证实例的唯一性

懒汉线程安全:使用synchronized导致性能缺陷

DCL:JVM即使编译器的指令重排序

静态内部类/枚举:延迟加载/线程安全/性能优势

外观模式

外观模式的主要目的在于让外部减少与子系统内部多个模块的交互,从而让外部能够更简单得使用子系统。它负责把客户端的请求转发给子系统内部的各个模块进行处理。

使用场景

1)当你要为一个复杂子系统提供一个简单接口时。

2)客户程序与抽象类的实现部分之间存在着很大的依赖性

3)当你需要构建一个层次结构的子系统时

优点:

1)由于Facade类封装了各个模块交互的过程,如果今后内部模块调用关系发生了变化,只需要修改Facade实现就可以了。

2) Facade实现是可以被多个客户端调用的

public class ModuleA {
    public void testFuncA() {
        System.out.println("This is Function From 改变了");
    }
}
public class ModuleB {
    public void testFuncB() {
        System.out.println("This is Function From ModuleB");
    }
}
public class Facade {
    private ModuleA moduleA = null;
    private ModuleB moduleB = null;
    private ModuleC moduleC = null;
    private static Facade mFacade = null; 
    private Facade(){
        moduleA = new ModuleA();
        moduleB = new ModuleB();
        moduleC = new ModuleC();
    }   
    public static Facade getInstance() {
        if(mFacade == null) {
            mFacade = new Facade();
        }
        return mFacade;
    }
    
    public void testOperation() {
        moduleA.testFuncA();
        moduleB.testFuncB();
        moduleC.testFuncC();
    }
}
public class Client {
    public static void main(String arg[]) {
        Facade.getInstance().testOperation();
    }
}

装饰模式

  • 对一组对象的功能进行增强时,就可以使用该模式进行问题的解决,是一种对象结构型模式。装饰和继承都能实现一样的特点:进行功能的扩展增强。 但是只为提高功能,进行的继承,会导致继承体系越来越臃肿,不够灵活。
  • 与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统耦合性不高,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。

使用场景

  (1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  (2)当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式

  • Component: 抽象构件
  • ConcreteComponent: 具体构件
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类

ConcreteDecorator和Decorator都是继承component

public class DecoratorExample {

    /**
     * 抽象构件
     */
    interface Component {
        void operation();
    }

    /**
     * 具体构件
     */
    class ConcreteComponent implements Component {

        @Override
        public void operation() {
            System.out.println("from ConcreteComponent");
        }
    }

    /**
     * 抽象装饰类
     */
    class Decorator implements Component {
        private Component component;  //维持一个对抽象构件对象的引用

        public Decorator(Component component) { //注入一个抽象构件类型的对象
            this.component = component;
        }

        @Override
        public void operation() {
            component.operation();  //调用原有业务方法
        }
    }

    /**
     * 具体装饰类
     */
    class ConcreteDecoratorA extends Decorator {
        public ConcreteDecoratorA(Component component) {
            super(component);
        }

        @Override
        public void operation() {
            //俩方法位置随便替换
            super.operation();  //调用原有业务方法
            addedBehavior();  //调用新增业务方法
        }

        //新增业务方法
        public void addedBehavior() {
            System.out.println("from ConcreteDecoratorA");
        }
    }

    class ConcreteDecoratorB extends Decorator {
        public ConcreteDecoratorB(Component component) {
            super(component);
        }

        @Override
        public void operation() {
            super.operation();  
            addedBehavior();  
        }

        //新增业务方法
        public void addedBehavior() {
            System.out.println("from ConcreteDecoratorB");
        }
    }

    public static void main(String[] args) {
        //为了方便看就写到一个类里了,理解意思即可
//        ConcreteComponent concreteComponent = new ConcreteComponent();
//        Decorator decorator = new Decorator(concreteComponent);
//        ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(decorator);
//        ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
//        concreteDecoratorB.operation();

    }
}

工厂模式

工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。 

工厂类可以根据条件生成不同的子类实例,并且这些对象需要具有共同的接口。

工厂方法模式(Factory Method)分为3种:

1、普通工厂模式

就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:

我们以一个例子来讲解:发送短信和发送邮件(具有共同的接口:发送)

  public interface Sender {  
    /* 
     * 发送邮件或者短消息的共同接口 
     */  
    public void sender();  
}  

/* 
 * 发送邮件的实现类 
 */  
public class MailSender implements Sender {  
  
    public void sender() {  
        // TODO Auto-generated method stub  
        System.out.println("this is mailsender!");  
    }  
}
/* 
 * 发送短信的实现类 
 */  
public class SMSSender implements Sender {  
  
    public void sender() {  
        // TODO Auto-generated method stub  
        System.out.println("this is sms sender!");  
    }  
  
} 
public class SendFactory {  
    public Sender produce(String type){  
        if ("mial".equals(type)) {  
            //根据类型生产对象  
            return new MailSender();  
        }else if("sms".equals(type)) {  
            //根据类型生产对象  
            return new SMSSender();  
        }else {  
            System.out.println("类型输入错误");  
            return null;  
        }  
    }  
}  

工厂方法模式:这是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:

public class SendFactory {  
      
    public Sender produceMail(){  
        return new MailSender();  
    }  
      
    public Sender produceSms(){  
        return new SmsSender();  
    }  
}  
public class FactoryTest {  
  
    public static void main(String[] args) {  
        SendFactory factory = new SendFactory();  
        Sender sender = factory.produceMail();  
        sender.Send();  
    }  
} 

静态工厂模式:将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可

public class SendFactory {  
      
    public static Sender produceMail(){  
        return new MailSender();  
    }  
      
    public static Sender produceSms(){  
        return new SmsSender();  
    }  
}  
public class FactoryTest {  
  
    public static void main(String[] args) {      
        Sender sender = SendFactory.produceMail();  
        sender.Send();  
    }  
} 

工厂模式适用于:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

抽象工厂模式(Abstract Factory)

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,我们先看看图,然后就和代码,就比较容易理解。

这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!

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 SendMailFactory implements Provider {  
      
    @Override  
    public Sender produce(){  
        return new MailSender();  
    }  
}  

public class SendSmsFactory implements Provider{  
  
    @Override  
    public Sender produce() {  
        return new SmsSender();  
    }  
}  
public interface Provider {  
    public Sender produce();  
} 
public class Test {  
  
    public static void main(String[] args) {  
        Provider provider = new SendMailFactory();  
        Sender sender = provider.produce();  
        sender.Send();  
    }  
}

相关源码:

https://github.com/peiniwan/DesignPattern.git

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏喔家ArchiSelf

从构造函数看线程安全

线程是编程中常用而且强大的手段,在使用过程中,我们经常面对的就是线程安全问题了。对于Java中常见的数据结构而言,一般的,ArrayList是非线程安全的,Ve...

1082
来自专栏SDNLAB

POF技术分享(三):Packet处理流程

前言: 之前对POF基本原理、POF交换机源码结构进行解读,但是,要想完成POF交换机的二次开发和拓展,有必要对POF交换机特有的数据包处理流程、POF交换机和...

37212
来自专栏大内老A

采用一个自创的"验证框架"实现对数据实体的验证[编程篇]

昨天晚上突发奇想,弄了一个简易版的验证框架,用于进行数据实体的验证。目前仅仅实现基于属性的声明式的验证,即通过自定义特性(Custom Attribute)的方...

2606
来自专栏Spark生态圈

[spark] Shuffle Read解析 (Sort Based Shuffle)

本文将讲解shuffle Reduce部分,shuffle的下游Stage的第一个rdd是ShuffleRDD,通过其compute方法来获取上游Stage S...

1181
来自专栏程序员维他命

iOS 代码规范

花了一个月的时间结合几篇博客和书籍写了这套 iOS 代码规范(具体参考底部的参考文献部分)。这套代码规范除了有仅适用于 iOS 开发的部分,还有其他的比较通用性...

1912
来自专栏iOS开发

iOS开发之 Method Swizzling 深入浅出

如果产品经理突然说:"在所有页面添加统计功能,也就是用户进入这个页面就统计一次"。我们会想到下面的一些方法:

4767
来自专栏java学习

Java每日一练(2017/8/2)

本期题目: (单选题)1、在 java 中,一个类可同时定义为许多同名的方法,这些方法的形式参数个数,类型或顺序各不相同,传回的值可能个不相同,这种面向对象的...

2797
来自专栏mini188

java中的锁

java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够。于是再次翻看了一下书里的内容,突然有点打开脑门的感觉...

3919
来自专栏我就是马云飞

LruCache源码解析

 今天我们来聊聊缓存策略相关的内容,LruCache应该说是三级缓存策略会使用到的内存缓存策略。今天我们就来扒一扒这里面的原理,同时也温故温故我们的数据结构方面...

2277
来自专栏YG小书屋

Hadoop Streaming 读ORC文件

hadoop Streaming的处理流程是先通过inputFormat读出输入文件内容,将其传递mapper,再将mapper返回的key,value传给re...

1522

扫码关注云+社区

领取腾讯云代金券