用最简单的例子说明设计模式(一)之单例模式、工厂模式、装饰模式、外观模式
设计模式
- 所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。
- 在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; } }
静态内部类单例(推荐)
静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonHoler.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