Java设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
一、设计模式的分类
总体来说设计模式分为三大类(23种):
创建型模式,共五种:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类特殊的模式:并发型模式和线程池模式。
二、设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三、Java的23中设计模式的概述
(一)工厂方法模式(Factory Method)
工厂模式的好处就是分工明确!
工厂方法模式分为两种:
1.普通工厂模式,
就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
2.多个工厂方法模式,
是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
(二)抽象工厂模式(Abstract Factory)
创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
(三)单例模式(Singleton)
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。
单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
1.懒汉式单例
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能
通过getInstance()方法访问。但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例。
2.饿汉式单例
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
3.登记式单例
实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。这个用得最少。
(四)建造者模式(Builder)
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,
(五)原型模式(Prototype)
原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
(六)适配器模式(Adapter)
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
(七)装饰模式(Decorator)
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
(八)代理模式(Proxy)
代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
(九)外观模式(Facade)
外观模式是为了解决类与类之家的依赖关系的,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口。
(十)桥接模式(Bridge)
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
(十一)组合模式(Composite)
组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便。
(十二)享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。
(十三)策略模式(strategy)
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。
(十四)模板方法模式(Template Method)
模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。
(十五)观察者模式(Observer)
观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。
(十六)迭代子模式(Iterator)
迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。
(十七)责任链模式(Chain of Responsibility)
责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。
(十八)命令模式(Command)
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
(十九)备忘录模式(Memento)
主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。
(二十)状态模式(State)
当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。
(二十一)访问者模式(Visitor)
访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
(二十二)中介者模式(Mediator)
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行。
(二十三)解释器模式(Interpreter)
解释器模式用来做各种各样的解释器,如正则表达式等的解释器等等。
上面就是Java的设计模式的概述。
但是其实在开发中有很多模式是用不到的,比如我是专门做Android开发的,用到的模式基本上就是单例模式,工程模式,适配器模式这些简单的模式。
对于设计模式的名称和作用还是要有点了解的,有些代码的底层设计就是使用设计模式来完成的,学习设计模式还是很有作用的。
1、工厂方法模式
**定义:**Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 也就是说,我们定义一个接口用于创建对象,但让子类去决定实例化哪一个对象。换句话说,就是工厂方法将对象的实例化工作转交给了它的子类,而工厂类本身只定义对象实例化规范。
示例简介:通过工厂类接口,创建多个不同的产品类实例。
类图:
其中Product为产品抽象类,其具体产品类ProductA与ProductB为其子类;ProductFactory为抽象工厂接口,提供了创建产品对象的方法,ConcreteProductFactory类为该接口的实现类。通过传入对应Class对象,调用createProduct()方法返回对应产品实例对象。其具体代码如下:
Product.java
/**
* 抽象产品类
*/
public abstract class Product {
public abstract void introduction();
}
ProductA.java
/**
* 具体产品类
*/
public class ProductA extends Product {
@Override
public void introduction() {
System.out.println("这是产品A");
}
}
ProductB.java
/**
* 具体产品类
*/
public class ProductB extends Product {
@Override
public void introduction() {
System.out.println("这是产品B");
}
}
ProductFactory.java
/**
* 工厂接口
*/
public interface ProductFactory {
//创建实例接口
public <T extends Product> T createProduct(Class<T> c);
}
ConcreteProductFactory.java
/**
* 工厂接口实现类
*/
public class ConcreteProductFactory implements ProductFactory {
public <T extends Product> T createProduct(Class<T> c) {
Product product;
try{
product = (Product) Class.forName(c.getName()).newInstance();
}catch (Exception e){
return null;
}
return (T)product;
}
}
测试代码
/**
*/
public class Main {
public static void main(String [] args){
Product product;
//创建产品工厂实例
ProductFactory factory = new ConcreteProductFactory();
//获取产品A实例
product = factory.createProduct(ProductA.class);
product.introduction();
//获取产品B实例
product = factory.createProduct(ProductB.class);
product.introduction();
}
}
结果:
优点:工程方法模式拥有良好的封装性,代码结构很清晰,获取产品对象只需要知道类名就够了,不用知道其创建过程。另外,工厂方法模式拥有良好的扩展性,只需适当增加产品类与修改工厂类或新增工厂类,就能够适应新的环境。
2、抽象工厂模式
定义:Provide an interface for creating families of related or dependent objects without specifying their concrete classes. 即为一组相关或相互依赖的对象提供一个实例化接口,而且无需指定他们的具体类。说白了,相对于之前提到的工厂方法模式,抽象工厂模式在创建对象实例时无需传入任何参数,即建立起与待实例化对象的一一对象关系,而在工厂模式中createProduct(Class c)方法还需传入一个具体的Class类名,所以工厂模式可以一对多。 差别仅此而已。下面用一个例子说明:
类图:
对比工厂模式,很容易理解这种模式,废话不多说,直接上测试代码:
AbstractFactory.java
/**
* 抽象工厂类
*/
public abstract class AbstractFactory {
//创建A类产品实例
public abstract AbstractProduct createProductA();
//创建B类产品实例
public abstract AbstractProduct createProductB();
}
AbstractProduct.java
/**
* 抽象产品类
*/
public abstract class AbstractProduct {
//自我介绍
public abstract void introduction();
}
ProductA.java
/**
* A类产品
*/
public class ProductA extends AbstractProduct {
@Override
public void introduction() {
System.out.println("这是产品A");
}
}
ProductB.java
/**
* B类产品
*/
public class ProductB extends AbstractProduct {
@Override
public void introduction() {
System.out.println("这是产品B");
}
}
ConcreteFactory.java
/**
* 具体工厂实现类
*/
public class ConcreteFactory extends AbstractFactory {
@Override
public AbstractProduct createProductA() {
return new ProductA();
}
@Override
public AbstractProduct createProductB() {
return new ProductB();
}
}
测试代码:
public class Main {
public static void main(String [] args){
//创建具体工厂
AbstractFactory factory = new ConcreteFactory();
//获取产品实例
ProductA productA = (ProductA) factory.createProductA();
ProductB productB = (ProductB)factory.createProductB();
//打印产品信息
productA.introduction();
productB.introduction();
}
}
测试结果:
在这个例子中,抽象工厂方法中分别定义了创建A类产品以及B类产品实例的方法,如果还有C类产品,则在抽象工厂类中继续添加创建C类产品的方法,如果是还有A1,A2…An,B1,B2…Bn系的产品呢?则新建n个继承自抽象工厂的子类,第n个工厂实现类创建A类以及B类产品实例方法分别返回An以及Bn产品类实例。这也就是抽象工厂模式定义里提到的创建一组相关或相互依赖的对象。
优点:封装性较工厂方法模式更好一点,毕竟创建对象实例时连类名都不用知道。缺点也在于这一点,创建对象实例的方法只能是一对一的关系,导致系统的复杂性增加了。另外,如果一开始只在抽象工厂类里定义了A和B类产品创建实例的方法,后面也多了个C类产品,这时候就需要新增C类产品实例的抽象创建方法,此时将要对所有抽象工厂的子类进行改写,这个也是比较麻烦的。
应用环境:这个也很明显,就是应用于在需要创建明确几组对象的时候。
3、装饰模式
定义:Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality. 简而言之就是,在保持同一个功能接口不变的情况下动态的为对象添加新的功能,就被称为装饰模式。
理解装饰模式:其实装饰模式关键也是对象的持有,代理模式也是把操作对象传入代理类中,调用代理类来給实际对象做事情,从装饰模式的定义中说到:給一个对象添加额外的职责。这里把职责定义一个接口,并定义需要的方法,让另一个装饰角色来继承这个接口,并持有需要装饰的对象,这样就能得到一个经过装饰的对象。并且这里的职责接口定义是不需要知道具体对象的,他也是可以针对其他相关的对象来进行修饰。把你需要装饰的对象传进去就可以了。
故事:下面将用一个例子说明采用装饰模式来灵活为对象增加功能的方便之处。故事情景为推销一本书。如何才能灵活不断地添加新的推销词,来“装饰”你这本书,这就是我们装饰模式所要做的。
一般方式:采用添加子类的方式,覆写父类的推销方法来达到修饰原有说辞的目的。然而一旦需要再次添加新的修饰词,按照这种思路我们只能不断地继承子类,覆写上一级父类的推销方法,达到我们的目的。采用这种方式,一旦类之间的继承关系超过三重,乃至更多,一旦我们想更改以前的功能模块,则调试起来将极为不便。这种传统思维下,系统的可扩展性将极差。
装饰模式::其基本思想在于:首先创建一个抽象类或接口,作为待修饰的基本核心类,例如本例中的书,定义推销方法promote(),然后,由继承该抽象类(或接口),创建具体的待修饰实体类,例如本例中的小说,以及采用代理模式的思想创建修饰类的抽象父类。
具体修饰类均继承自该修饰类的抽象父类,并覆写父类方法,达到修饰的作用。
类图如下:
其代码如下:
Compoment.java
/**
* 核心抽象基础类
*/
public abstract class Component {
//推销方法
public abstract void promote();
}
ConcreteComponent.java
/**
* 具体待装饰类
*/
public class ConcreteComponent extends Component {
@Override
public void promote() {
System.out.println("这是一本书,售价99元");
}
}
Decorator.java
/**
* 装饰基类,采用代理模式,传入待装饰对象
*/
public class Decorator extends Component {
//装饰对象
private Component component;
//构造函数
public Decorator(Component component){
this.component = component;
}
@Override
public void promote() {
this.component.promote();
}
}
NumDecorator.java
/**
* 销量装饰器
*/
public class NumDecorator extends Decorator {
public NumDecorator(Component component){
super(component);
}
@Override
public void promote() {
super.promote();
System.out.println("本书销量本年度第一,达到一亿册哟!!");
}
}
PriceDecorator.java
/**
* 价格装饰类
*/
public class PriceDecorator extends Decorator {
//构造函数
public PriceDecorator(Component component){
super(component);
}
@Override
public void promote() {
super.promote();
System.out.println("原价可是999元哦!!");
}
}
测试类:
/**
*/
public class Main {
public static void main(String [] args){
Component component;
//得到基本待修饰对象
component = new ConcreteComponent();
//首次修饰
component = new PriceDecorator(component);
//再一次修饰
component = new NumDecorator(component);
//打印修饰后的推销辞令
component.promote();
}
}
结果:
模式优点:
系统扩展性非常好,在需要“打补丁”的应用环境下,它可以作为采用多级继承关系的替代方案。装饰类和待装饰类可以相互独立发展,而不会相互耦合。
装饰模式可以动态地扩展一个类的功能
注意事项:还是需要尽量减少装饰类的数量,否则系统复杂性将大幅度 提升,在多层装饰模式下,排查问题所耗用的时间也将激增。
总结:在想到采用继承扩展已有类的功能时,请立刻想到更好地去采用装饰模式实现。
4、单例模式:
单例模式算是java设计模式中最简单的一个了。
别人问你对单例模式的理解,你可以这样说:
单例模式确保一个类只能有一个实例,不论再任何类内调用这个类的对象,都是调用同一个对象,并且这个类不能new出来,因为构造方法已经私有化了,只能用静态方法调用这个对象。
单例模式的作用:确保整个程序调用的对象是一致的,里面的某个数据是全局的,无论在哪一个页面改变数据,其他任何页面都是获取到这个最新的数据。
下面开始全面介绍一下,单例模式:
单例模式概念:
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例(很少用)。
单例模式有以下特点:
1.单例类只能有一个实例。
2.单例类必须自己创建自己的唯一实例。
3.单例类必须给所有其他对象提供这一实例。
懒汉式单例模式:
懒汉式,说明这个类是很懒的,刚开始什么也不做,只有第一次使用才去实例化对象,后面使用的一直是这个对象。代码:
public class Singleton {
private Singleton() {
}
private static Singleton single = null;//静态工厂方法
/**
* 懒汉式实例化对象,第一次使用才去实例化
*/
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。
但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例。正确的做法是在getInstance方法上加同步。
//加同步锁synchronized,
public synchronized static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
上面使用的是同步方法,也是可以使用同步代码块,结果是一样的。同步的作用是防止多个类同时调用这个类时,会创建多个对象!
饿汉式单例模式:
饿汉嘛,说明这个类是很饿的,一来到就要吃东西,所有东西都要事先准备好的。
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
代码:
public class Singleton {
private Singleton() {
}
private static final Singleton single = new Singleton();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}
登记式单例模式:
登记单例模式是使用HashMap存储的,代码麻烦,累赘,不建议看和用。这里只是給大家简单展示一下。
代码:
public class Singleton3 {
private static Map<String, Singleton3> map = new HashMap<String, Singleton3>();
static {
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造方法
protected Singleton3() {
}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if (name == null) {
name = Singleton3.class.getName();
System.out.println("name == null" + "--->name=" + name);
}
if (map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。
这里对登记式单例标记了可忽略,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。
饿汉式和懒汉式区别
从名字上来说,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
另外从以下两点再区分以下这两种方式:
1.线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,需要同步,加载和性能方面有些区别。
2.资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
结论:
由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
Android开发中也是经常用到单例模式
Android开发中的单例模式一般都是在继承Application类中实现的。
代码:
/**
* Application类
*/
public class App extends Application {
private static App sInstance;//全局对象
/**
* 全局获取单例
*/
public static App getInstance() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
//赋值
sInstance = this;
}
//定义一些全局变量和全局方法
}
Android中Application和Activity都是比较特殊的,都没有构造方法,都是不能new出来的,Application只要在AndroidManifest中注册,就会执行onCreate方法,这时就已经有这个全局对象了,并且不需要什么同步,不需要考虑什么懒汉、饿汉(其实是饿汉),因为Application是这个程序最先运行的类,其他任何调用类都是在之后运行的。
关于单例模式,记住最重要的一句话就可以:在一个程序中,这个类的对象是全局唯一。
5、代理模式
代理模式,听过的人也是不多,但是根据它的名字你就可以想象到他的大概作用。代理就是它代你做一些事情!代码和应用理解起来也是不难的,本文就带你深入了解一下。
代理模式是java设计模式中三大类的第二类结构型模式。
代理模式现实场景
代理模式,理论上理解也是比较简单的,比如,你买车了,你要去办车牌,你可以自己去,也可以叫汽车销售公司代你去,但是你要把一些身份相关证明和一些表格填好給他们,他们代你去办理,其实相当于你自己办理,因为证件已经齐全了,他们就能办好了。这就是代理模式。还有网上抢票,因为你已经在他们的网站进行过验证,所有他们买票,就相当于你买票了,也是因为他们有你的身份信息和购买请求。
把你的信息給别人,别人帮你办事,这就是代理模式!
代理模式(Proxy Pattern)的定义
代理模式:为其他对象提供一种代理以控制这个对象的访问。
Proxy是代理的意思,Pattern是模式。
简单点理解就是把你的对象給代理者,代理者帮你做事。
代理模式的类图
代理模式涉及到的三种角色
1.抽象主题(Subject)角色
该角色是真实主题的代理主题的共同接口,以便在任何可以使用真实主题的地方可以使用代理主题。
2.真实主题(RealSubject)角色
该角色也叫委托角色或被代理角色,是业务逻辑的具体执行者。
3.代理主题(ProxySubject)角色
也叫委托类、代理类,该角色负责控制类对真实主题的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后做预处理和善后工作。
上面的角色概念如果不理解,先看实例,返回来看就理解了。
从上面类图中可以看到代理主题是依赖于真实主题的,因为没有真实主题对象,代理主题是没事实际意义的。
代理模式的优点
1.职责清晰,真实角色只需要实现实际的业务逻辑,而不用关心其他非本职的事物。
比如,你知道买车牌要身份证和相关证件,但是去什么地方,需要跑多少路,排队要多久,你都不需要关心,代理人已经帮你去搞好了。
2.高扩展性
具体角色随需求不同可能有很多种,但是只要代理角色实现了接口,代理模式就完成可以在不做任何修改的情况下代理各种真实的主题角色。
上面一句话其实是说每个具体角色都会有一个抽象主题,但是代理模式可以有多个抽象主题,这个代理主题就可以給多个真实主题做事情。
3.代理模式的应用
代理模式其实在我们编程中是基本都会遇到的,比如很多网络框架,你传递給它一个参数对象Param,框架会从参数里面获取对象的数据,做很多其他的处理。
还有很多其他类似的框架也是要传入对象,然后,里面帮你做很多事情的。
还有我们也会传入一个对象到另一个类中,另一个类做很多判断,处理等等。
不管是别人的框架还是我们写的代码都是有很多代理模式相关的,只是我们没有意识到,看完本文后,你在以后的代码中多观察,是会发现很多这种需求的。
代理模式是一项基本的设计模式,许多比如状态模式、策略模式、访问者模式本质也是采用了代理模式。后面会給大家一一详解。
代理模式的实例
这里就以代理买车牌为示例
代理模式的实例类图
这里代理主题其实可以是还有很多操作方法,比如具体去哪里办车牌,給谁付钱等等步骤/方法,这里简化了。
各个接口/类的代码
1.抽象主题,IBuyCarNunber
定义购买车牌必须要的步骤方法
package p6_proxy;
/**
* 购买车牌的接口类
* 定义购买需要的相关手续
*/
public interface IBuyCarNunber {
public void carPlace(String city);//车牌的城市
public void validate();//验证购买者的身份
public void payMoney();//付钱
}
2.真实主题,实际要购买车牌者CarNumberBuyer
可以执行操作的有效对象
package p6_proxy;
/**
* 实际要购买车牌的人
* 如果自己购买是要经过下面几个具体步骤的
*/
public class CarNumberBuyer implements IBuyCarNunber {
String name;
public CarNumberBuyer(String name) {
this.name = name;
}
@Override
public void carPlace(String city) {
System.out.println("选择城市:" + city);
}
@Override
public void validate() {
System.out.println(name + "的有效证件");
}
@Override
public void payMoney() {
System.out.println(name + "支付1000元");
}
}
3.代理主题,,代理购买车牌者CarNumberProxy
package p6_proxy;
/**
* 购买车牌号的代理商
* 实际是通过购买人的对象类购买车牌
*/
public class CarNumberProxy implements IBuyCarNunber {
CarNumberBuyer carNumberBuyer;
/**
* 在构造方法种传入购买人的对象
* 后面的操作,代理人操作的方法,其实就是购买人自己操作
* 当然不用构造方法也是可以的,可以多写一个公开方法传入购买人的对象
*/
CarNumberProxy(CarNumberBuyer carNumberBuyer) {
this.carNumberBuyer = carNumberBuyer;
}
@Override
public void carPlace(String city) {
carNumberBuyer.carPlace(city);
}
@Override
public void validate() {
carNumberBuyer.validate();
}
@Override
public void payMoney() {
carNumberBuyer.payMoney();
}
}
4.代理模式的调用者ProxyDemo
package p6_proxy;
/**
* 代理模式的调用示例
*/
public class ProxyDemo {
public static void main(String[] args) {
CarNumberBuyer buyer = new CarNumberBuyer("李文");//创建购买人对象
CarNumberProxy proxy = new CarNumberProxy(buyer);//创建代理人对象,并把购买人对象传进去
proxy.carPlace("广东深圳");//代理人设置购买的车牌城市
proxy.validate();//代理人车牌人验证
proxy.payMoney();//代理人付钱
}
}
上面也是可以把代理模式中代理者的几个步骤封装在代理者对象的一个方法里面,这样你在外面看到代理者,只做一步就把你所有事情搞定了,看你需求吧。
看完上面的一个实际实例,有人可能会觉得我直接new一个实际操作对象,然后自己做那几步不就OK了吗,干嘛还要代理者和接口类,那么麻烦。这里也说明一下:这里只是一个简单的示例,如果真实主题有几十个步骤呢,而且判断很多呢,如果交给代理者可能只要看到几个步骤就可以完成了,并且逻辑可以处理得更加详细清晰。
到这里大家想必对代理模式有个比较清楚的认识了吧。
记住一句话,跟别人说就可以了:代理模式就是,把实际操作对象放到代理对象中,代理对象帮助实际对象做详细的处理。
当你你可以说详细点,代理模式就是代理对象类和实际对象类共同实现一个接口,接口定义了真实角色需要做一件事情必须步骤,然后创建代理对象的时候把真实对象传进去,这样就能实现代理者帮实际角色做很多判断和操作。
6、适配器模式
适配器模式,也叫变压器模式。在适配器模式下,变压器的作用是把一种电压变成另一种电压。
上面是用生活相关的词语解析。
其实适配器对我们Android程序员来说是非常非常熟悉的了,我们使用ListView就要用到适配器Adapter,这里适配器的作用就是把一些数据(经过一定的封装/排序),变成我们一列列的数据。
一.适配器模式的现实场景:人工翻译
王小华打算去图书馆借书,但是因为是新生,不熟悉路,正愁时对面走来一个外国朋友。王小华上前打听,可是英语水平有限,没有办法与对方进行交流。此时,他的同学李良刚好经过,而且他的英语口语很好,王小华请李良做他的翻译跟对方交流,最终他顺利知道去图书馆的路。
在上面场景中,王小华需要知道去图书馆的路,外国友人知道去图书馆的路,但是王小华听不懂英语,这是需要李良这个翻译把英语翻译过来,这里李良就相当于一个适配器的作用,把一种语言变成另一种需要的语言。
二.适配器模式(Adapter Pattern)的定义
将一个类的接口变成客户端所期待的另一个接口,从而使原本应接口不匹配而无法在一起工作的两个类能够在一起。
三.适配器模式的类图
四.适配器设计的三个角色
1.目标角色(Target):该角色定义要转换成的目标接口。
2.源角色(Adapee):需要被转换成目标角色的源角色。
3.适配器角色(Adapter):该角色是适配器模式的核心,其职责是通过继承或者类相关的方式,将源角色转换成目标角色。
五.适配器的优点
1.适配器可以让两个没有任何广西的类在一起运行。
2.增加了类的透明性
3.增加了类的复用性
4.增加了代码的灵活性
六.适配器模式的使用场景
修改一个已经投产的系统时,需要对系统进行扩展,如果需要一个已有的类进行扩展,这个类不符合接口,这时使用适配器模式是最适合的,这样可以将一个不符合系统接口的类转换成符合系统接口、可以使用的类。
适配器模式的实例
这里以文中开头的现实场景:将中文转换为英文做实例代码设计。
实例类图
![2](https://i.imgur.com/2AYFoFj.png)
这里和上面适配器模式类图的样式有点不一样。
适配器角色可以继承源角色关联源角色,也是可以把适配器角色对象放到源角色中进行相关。
实例代码
1.目标角色IEnglish
package p8_adapter;
/**
* 目标角色
* 定义源角色要变成上面样子的规范接口
*/
public interface IEnglish {
public void speakEnglish(String chinese);
}
2.适配器角色TranslaterAdapter
package p8_adapter;
/**
* 语言适配器
* 将中文转化成英文
* 这里的适配器是做实际的操作
*/
public class TranslaterAdapter implements IEnglish {
/**
* 将中文翻译成英文的一个类
*/
@Override
public void speakEnglish(String chinese) {
System.out.println("适配器把中文:‘" + chinese + "’翻译成英文:");
System.out.println("Hello,where is the library?");
}
}
3.源角色Chinese
package p8_adapter;
/**
* 源角色,没经过转换前的对象数据
*/
public class Chinese {
String msg;
//构造方法
public Chinese(String message) {
System.out.println("问:" + message);
msg = message;
}
//添加适配器,并要适配器做操作
public void setAdatper(IEnglish english) {
english.speakEnglish(msg);
}
}
4.适配器测试类AdapterDemo
package p8_adapter;
/**
* 适配器模式测试类
*/
public class AdapterDemo {
public static void main(String[] arg) {
Chinese chinese = new Chinese("请问图书馆怎么走??");//创建源对象
IEnglish adapter = new TranslaterAdapter();//创建适配器对象,这里用多态
chinese.setAdatper(adapter);//把适配器对象放到源对象做操作
}
}
到这里适配器模式就简单介绍完了。
但是鉴于我们Android开发者对ListView的适配器Adapter的熟悉,我还是对Android适配器和设计模式的适配器做一些个人理解说給大家听:
我们android经常使用的Adapter:BaseAdapter、ArrayAdapter、SimpleAdapter。
BaseAdapter就像适配器模式中的目标角色,定义了基本接口和方法,你实现了BaseAdapter后实现相关的方法就可以得到适配器角色。接着就可以給适配器放入数据进行整合。
而ArrayAdapter和SimpleAdapter已经是适配器角色,可以直接放入数据,实现数据的整理功能。
通过适配器的方法接收把数据源对象的数据,刷新适配器。
来源:https://blog.csdn.net/u013248535/article/list
https://blog.csdn.net/wenzhi20102321/article