java设计模式
Abstract Factory 抽闲工厂模式——提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
Adapter 适配器模式—–将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Bridge 桥接模式——将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Builder 生成器模式——将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Chain of Responsibility 职责链模式——为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
Command 命令模式——将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
Composite 组合模式——–将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。
Decorator 装饰模式——动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator模式比生成子类方式更为灵活。
Facade 外观模式——为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method 工厂方法模式——定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
Flyweight 享元模式——运用共享技术有效地支持大量细粒度的对象。
Interpreter 解释器模式——给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
Iterator 迭代器模式—–提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
Mediator 中介者模式——用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Memento 备忘模式——在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
Observer 观察者模式:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
Prototype 原型模式——用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
Proxy 代理模式:为其他对象提供一个代理以控制对这个对象的访问。
Singleton 单态模式——保证一个类仅有一个实例,并提供一个访问它的全局访问点。
State 状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
Strategy 策略模式——定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
Template Method 模板方法模式——定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Visitor 访问者模式—–表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
二、事例:
1.创建型模式
FACTORY?加工工厂:给它“M4A1”,它给你产把警枪,给它“AK47”,你就端了把匪枪。CS里买枪的程序一定是用这个模式的。
BUILDER?生产流水线:以前是手工业作坊式的人工单个单个的生产零件然后一步一步组装做,好比有了工业革命,现在都由生产流水线代替了。如要造丰田汽车,先制定汽车的构造如由车胎、方向盘、发动机组成。再以此构造标准生产丰田汽车的车胎、方向盘、发动机。然后进行组装。最后得到丰田汽车;
PROTOTYPE?印刷术的发明:以前只能临贴抄写费时费力,效率极低,有了印刷术,突突的;
SINGLETON?确保唯一:不是靠new的,是靠instance的,而且要instance地全世界就这么一个实例(这可怜的类,也配叫“类”)。 看SingleTon类代码。
2.结构型模式
ADAPTER?翻译官:胡哥只会汉语,布什只会美语,翻译官既通汉又通美,Adapter了 ;
DECORATOR?装饰:名字可以标识一个人,为了表示对一个人的尊重,一般会称其为“尊敬的”,有了装饰,好看多了;
BRIDGE?白马非马:马之颜色有黑白,马之性别有公母。我们说"这是马"太抽象,说"这是黑色的公马"又太死板,只有将颜色与性别和马动态组合,"这是(黑色的或白色的)(公或母)马"才显得灵活而飘逸,如此bridge模式精髓得矣。
COMPOSITE?大家族:子又生孙,孙又生子,子子孙孙,无穷尽也,将众多纷杂的人口组织成一个按辈分排列的大家族即是此模式的实现;
FACADE?求同存异:高中毕业需读初中和高中,博士也需读初中和高中,因此国家将初中和高中普及成九年制义务教育;
FLYWEIGHT?一劳永逸:认识三千汉字,可以应付日常读书与写字,可见头脑中存在这个汉字库的重要;
PROXY?垂帘听政:犹如清朝康熙年间的四大府臣,很多权利不在皇帝手里,必须通过辅佐大臣去办;
3.行为模式
CHAIN OF RESPONSIBLEITY?租房:以前为了找房到处打听,效率低且找不到好的房源。现在有了房屋中介,于是向房屋中介提出租房请求,中介提供一个合适的房源,满意则不再请求,不满意继续看房,直到满意为止;
COMMAND?借刀杀人:以前是想杀谁就杀,但一段时间后领悟到,长此以往必将结仇太多,于是假手他人,挑拨他人之间的关系从而达到自己的目的;
INTERPRETER?文言文注释:一段文言文,将它翻译成白话文;
ITERATOR?赶尽杀绝:一个一个的搜索,绝不放掉一个;
MEDIATOR?三角债:本来千头万绪的债务关系,忽出来一中介,包揽其一切,于是三角关系变成了独立的三方找第四方中介的关系;
MEMENTO?有福同享:我有多少,你就有多少;
OBSERVER?看守者:一旦被看守者有什么异常情况,定会及时做出反应;
STATE?进出自由:如一扇门,能进能出,如果有很多人随时进进出出必定显得杂乱而安全,如今设一保安限制其进出,如此各人进出才显得规范;
STRATEGY?久病成良医:如人生病可以有各种症状,但经过长期摸索,就可以总结出感冒、肺病、肝炎等几种;
TEMPLATE METHOD?理论不一定要实践:教练的学生会游泳就行了,至于教练会不会则无关紧要;
VISITOR?依法治罪:因张三杀人要被处死,李四偷窃要被罚款。由此势必制定处罚制度,故制定法律写明杀人、放火、偷窃等罪要受什么处罚,经通过后须变动要小。今后有人犯罪不管是谁,按共条例处罚即是,这就是访问者模式诞生的全过程。
(1)、普通工厂模式
一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。首先看下关系图:
举例如下:(我们举一个发送邮件和短信的例子)
首先,创建二者的共同接口:
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 { System.out.println("请输入正确的类型!"); return null; } } }
我们来测试下:
public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produce("sms"); sender.Send(); } }
(2)多个工厂方法模式
该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
将上面的代码做下修改,改动下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(); } }
输出:this is mailsender!
(3)静态工厂方法模式
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
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(); } }
输出:this is mailsender!
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
2、抽象工厂模式(Abstract Factory)
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,我们先看看图,然后就和代码,就比较容易理解。
请看例子:
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(); } }
其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!
3、单例模式(Singleton)
单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到。 数据库对象(SQLiteOpenHelper)等都会用到单例模式。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
一、作用
单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点
二、适用场景
1. 应用中某个实例对象需要频繁的被访问。
2. 应用中每次启动只会存在一个实例。如账号系统,数据库系统。
三、常用的使用方式
(1)懒汉式
优点:延迟加载(需要的时候才去加载)
缺点: 线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。
具体实现如下:
public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */ private static Singleton instance = null; /* 私有构造方法,防止被实例化 */ private Singleton() { } /* 1:懒汉式,静态工程方法,创建实例 */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
(2)加同步锁
优点:解决了线程不安全的问题。
缺点:效率有点低,每次调用实例都要判断同步锁
注:在Android源码中使用的该单例方法有:InputMethodManager,AccessibilityManager等都是使用这种单例模式。
具体代码如下:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
或
/*加上synchronized,但是每次调用实例时都会加载**/ public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; }
(3)双重检验锁
要优化(2)中因为每次调用实例都要判断同步锁的问题,很多人都使用下面的一种双重判断校验的办法。
优点:在并发量不多,安全性不高的情况下或许能很完美运行单例模式
缺点:不同平台编译过程中可能会存在严重安全隐患。
补充:在android图像开源项目Android-Universal-Image-Loader (https://github.com/nostra13/Android-Universal-Image-Loader)中使用的是这种方式。
/*3.双重锁定:只在第一次初始化的时候加上同步锁*/ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
这种方法貌似很完美的解决了上述效率的问题,它或许在并发量不多,安全性不太高的情况能完美运行,但是,这种方法也有不幸的地方。问题就是出现在这句
instance = new Singleton();
在JVM编译的过程中会出现指令重排的优化过程,这就会导致当 instance实际上还没初始化,就可能被分配了内存空间,也就是说会出现 instance !=null 但是又没初始化的情况,这样就会导致返回的 instance 不完整(可以参考:http://www.360doc.com/content/11/0810/12/1542811_139352888.shtml)。
(4)内部类的实现
优点:延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗。内部类是一种好的实现方式,可以推荐使用一下:
public class SingletonInner { private static class SingletonHolder { private static SingletonInner instance = new SingletonInner(); } /** * 私有的构造函数 */ private SingletonInner() { } public static SingletonInner getInstance() { return SingletonHolder.instance; } protected void method() { System.out.println("SingletonInner"); } }
(5)枚举的方法
这是网上很多人推荐的一种做法,但是貌似使用的不广泛,大家可以试试,具体代码如下:
public enum SingletonEnum { /** * 1.从Java1.5开始支持; * 2.无偿提供序列化机制; * 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候; */ instance; private String others; SingletonEnum() { } public void method() { System.out.println("SingletonEnum"); } public String getOthers() { return others; } public void setOthers(String others) { this.others = others; } }
4.代理模式
定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
应用场景举例:
通过上面的代理模式描述我们可以知道,其目的就是为了控制对象引用,生活场景中我们以买车为例,如果我们要买一辆轿车必须通过汽车4S店,汽车4s店就是充当代理角色,其目的就是控制买车客户的买车行为,必须通过汽车4S店才能从汽车厂商买一辆车。
1.)首先新建一个买车的接口
public interface IBuyCar { //买车 void buyCar(); }
2.)声明一个要买车的客户,实现买车接口
public class Customer implements IBuyCar { private int cash;//购车款 public int getCash() { return cash; } public void setCash(int cash) { this.cash = cash; } @Override public void buyCar() { Log.e("buyCar", "买一辆车花费了-->" + cash + "元"); } }
3.)声明一个买车代理汽车4S店,同样也实现买车接口,必须接受客户下单
public class BuyCarProxy implements IBuyCar{ private Customer customer;//接收买车客户 public BuyCarProxy(Customer customer){ this.customer=customer;//接收买车客户 } @Override public void buyCar() {//实现为客户买车 customer.buyCar(); } }
4.)创建一个客户端,模拟一次买车
Customer customer=new Customer(); customer.setCash(120000); BuyCarProxy buyCarProxy=new BuyCarProxy(customer); buyCarProxy.buyCar();
5.)通过代理模式实现权限控制
通过上面的例子,我们可能有个疑问,难道就不能直接去厂家买车吗?当然可以,如果在使用场景中实现类能满足要求时,我们当然可以直接实现类,但当实现类不能满足要求,要扩展需求,根据开闭原则你又不能修改实现类代码,这时你就用代理类。比如购买一辆车我们要对客户进行一个购车款审核,如果符合条件就买车,不符合要求我们就告知客户购车款不足。
@Override public void buyCar() {//实现为客户买车 int cash=customer.getCash(); if(cash<100000){ Log.e("buyCar","你的钱不够买一辆车"); return; } customer.buyCar(); }
实现场景
Customer customer=new Customer(); customer.setCash(120000); BuyCarProxy buyCarProxy=new BuyCarProxy(customer); buyCarProxy.buyCar(); Customer customer1 =new Customer(); customer1.setCash(90000); BuyCarProxy buyCarProxy1 =new BuyCarProxy(customer1); buyCarProxy1.buyCar();
动态代理机制:
以上讲的都是代理模式的静态实现,所谓静态代理就是自己要为要代理的类写一个代理类,或者用工具为其生成的代理类,总之,就是程序运行前就已经存在的编译好的代理类,这样有时候会觉得非常麻烦,也导致非常的不灵活,相比静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。
举例:还是接着上面的例子
1.)首先我们要声明一个动态代理类,实现InvocationHandler接口
public class DynamicProxy implements InvocationHandler { // 被代理类的实例 Object obj; // 将被代理者的实例传进动态代理类的构造函数中 public DynamicProxy(Object obj) { this.obj = obj; } /** * 覆盖InvocationHandler接口中的invoke()方法 * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构 * 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到 * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊 * 代码切入的扩展点了。 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /* * before :doSomething(); */ Object result = method.invoke(this.obj, args); /* * after : doSomething(); */ return result; } }
2.)具体实现
//我们要代理的真实对象 Customer customer = new Customer(); //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 InvocationHandler handler = new DynamicProxy(customer); /* * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象 * 第二个参数customer.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上 */ IBuyCar buyCar = (IBuyCar) Proxy.newProxyInstance(handler.getClass().getClassLoader(), customer.getClass().getInterfaces(), handler); buyCar.buyCar();
3.)动态代理好处
使用Java动态代理机制的好处:
1、减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
2、系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。