设计模式
一、常见的设计模式分类
设计模式可以根据创建型、结构型、行为型三种结构进行划分。
1.1 创建型
创建对象时,不再由我们直接实例化对象;而是根据特定场景,由程序来确定创建对象的方式,从而保证更大的性能、更好的架构优势。创建型模式主要有简单工厂模式(并不是23种设计模式之一)、工厂方法、抽象工厂模式、单例模式、生成器模式和原型模式。
1.2 结构型
用于帮助将多个对象组织成更大的结构。结构型模式主要有适配器模式adapter、桥接模式bridge、组合器模式component、装饰器模式decorator、门面模式、亨元模式flyweight和代理模式proxy。
1.3 行为型
用于帮助系统间各对象的通信,以及如何控制复杂系统中流程。行为型模式主要有命令模式command、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式state、策略模式、模板模式和访问者模式。
二、常用的设计模式讲解
2.1 创建型:单例模式
单例模式顾名思义是保证一个类只有一个实例,并且这个类自行实例化向整个系统提供。
UML图:
下面提供几种单例模式的写法:
java设计模式之 单例模式
单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式结构图中只包含一个单例角色:
Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。
单例模式是一种比较常见的设计模式。
单例模式作用:
1.控制资源的使用,通过线程同步来控制资源的并发访问;
2.控制实例产生的数量,达到节约资源的目的。
3.作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信。
单例模式适用场景
在以下情况下可以考虑使用单例模式:
(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。
(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
单例模式优缺点
主要优点:
A.提供了对唯一实例的受控访问。
B.由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
C.允许可变数目的实例。
主要缺点:
A.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
B.单例类的职责过重,在一定程度上违背了“单一职责原则”。
C.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
单例模式的七种书写方式
第一种:懒汉,线程不安全
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
第二种:懒汉,线程安全,效率不高
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
第三种:双重校验,提高效率
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
第四种:饿汉
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
第五种:饿汉,变种
public class Singleton { private Singleton instance = null; static{ instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return instance; } }
spring 的bean是单例模式、
2.2 创建型:工厂方法
2.1.1、简单工厂
就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
UML:
案例:
package com.roc.factory; /** * 产品的抽象接口 人类 * @author liaowp * */ public interface Human { public void say(); }
package com.roc.factory; /** * man 男人 * @author liaowp * */ public class Man implements Human { @Override public void say() { System.out.println("男人"); } }
package com.roc.factory; public class Woman implements Human { @Override public void say() { System.out.println("女人"); } }
package com.roc.factory; /** * 简单工厂
* if else可以换成Switch来实现,效果是一样的,对于实现类比较多Switch更实用。 */ public class SampleFactory { public static Human makeHuman(String type){ if(type.equals("man")){ Human man = new Man(); return man; }else if(type.equals("womman")){ Human woman = new Woman(); return woman; }else{ System.out.println("生产不出来"); return null; } } }
缺点:由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
2.2.2、工厂方法
提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。
可以一定程度上解耦,消费者和产品实现类隔离开,只依赖产品接口(抽象产品),产品实现类如何改动与消费者完全无关。
可以一定程度增加扩展性,若增加一个产品实现,只需要实现产品接口,修改工厂创建产品的方法,消费者可以无感知(若消费者不关心具体产品是什么的情况)。
UML:
/** * This is factory patter package */ package com.roc.factory; /** * 产品的抽象接口 人类 * @author liaowp * */ public interface Human { public void say(); } /** * This is factory patter package */ package com.roc.factory; /** * man 男人 * @author liaowp * */ public class Man implements Human { /* say method * @see com.roc.factory.Human#say() */ @Override public void say() { System.out.println("男人"); } } /** * This is factory patter package */ package com.roc.factory; /**女人 * @author liaowp * */ public class Woman implements Human { /* say method * @see com.roc.factory.Human#say() */ @Override public void say() { System.out.println("女人"); } } package com.roc.factory; /** * 工厂接口类 * @author liaowp * */ public interface Factory { public Human crateMan(); } package com.roc.factory; /** * 创造男人工厂类 * @author liaowp * */ public class ManFactory implements Factory{ public Human crateMan() { return new Man(); } } package com.roc.factory; /** * 创造女人工厂类 * @author liaowp * */ public class WomanFactory implements Factory{ @Override public Human crateMan() { // TODO Auto-generated method stub return new Woman(); } }
package com.roc.factory;
/**
* 抽象工厂测试
* @author liaowp
*
*/
public class Client {
public static void main(String[] args) {
Factory factory=new ManFactory();
Human man2=factory.crateMan();
man2.say();
}
}
2.2.3抽象产品
提供一个创建一系列的相关的或者依赖的对象的接口,无需指定它们的具体实现类,具体的时间分别在子类工厂中产生。也就是多个产品在同一个工厂中产生实例。定义多个产品接口,多个实现类,多个产品在同一个工厂中诞生。
package factory.simple; /** * 抽象产品角色 交通工具接口 * * @author lilin * */ public interface Car { /** * 上班函数 */ void gotowork(); }
package factory.abstractfactory; /** * @author lilin * */ public interface IBreakFast { /** * 吃早餐 */ void eat(); }
package factory.simple; /** * 具体产品角色,自行车 * * @author lilin * */ public class Bike implements Car { @Override public void gotowork() { System.out.println("骑自行车去上班!"); } }
package factory.simple; /** * @author lilin * */ public class Bus implements Car { @Override public void gotowork() { System.out.println("坐公交车去上班!"); } }
/** * */ package factory.abstractfactory; /** * @author lilin * */ public class Milk implements IBreakFast { @Override public void eat() { System.out.println("喝牛奶!"); } }
/** * */ package factory.abstractfactory; /** * @author lilin * */ public class Orange implements IBreakFast { @Override public void eat() { System.out.println("吃橘子!"); } }
/** * */ package factory.abstractfactory; import factory.simple.Car; /** * @author lilin * */ public interface IAbstractFactory { /** * * @return */ Car getCar(); /** * */ IBreakFast getBreakFast(); }
/** * */ package factory.abstractfactory; import factory.simple.Bike; import factory.simple.Car; /** * @author lilin * */ public class LowPersonFactory implements IAbstractFactory { @Override public Car getCar() { return new Bike(); } @Override public IBreakFast getBreakFast() { return new Orange(); } }
/** * */ package factory.abstractfactory; import factory.simple.Bus; import factory.simple.Car; /** * @author lilin * */ public class HighPersonFactory implements IAbstractFactory { @Override public Car getCar() { return new Bus(); } @Override public IBreakFast getBreakFast() { return new Milk(); } }
/** * */ package factory.abstractfactory; import org.testng.annotations.Test; import factory.simple.Car; /** * @author lilin * */ public class AbstractFactoryTest { @Test public void test() { IAbstractFactory factory = new LowPersonFactory(); Car car = factory.getCar(); IBreakFast breakFast = factory.getBreakFast(); System.out.println("吃的早饭是:"); breakFast.eat(); System.out.println("上班交通工具是:"); car.gotowork(); IAbstractFactory factory2 = new HighPersonFactory(); car = factory2.getCar(); breakFast = factory2.getBreakFast(); System.out.println("吃的早饭是:"); breakFast.eat(); System.out.println("上班交通工具是:"); car.gotowork(); } }
2.3 结构型:适配器模式
适配器模式:将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。
由图可知适配器模式包含一下三个角色:
1:Target(目标抽象类):目标抽象类定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类。在类适配器中,由于C#语言不支持多重继承,所以它只能是接口。
2:Adapter(适配器类):它可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。它是适配器模式的核心。
3:Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类包好了客户希望的业务方法。
1 public interface Ps2 { 2 void isPs2(); 3 } USB接口:Usb 1 public interface Usb { 2 void isUsb(); 3 } USB接口实现类:Usber 复制代码 1 public class Usber implements Usb { 2 3 @Override 4 public void isUsb() { 5 System.out.println("USB口"); 6 } 7 8 } 复制代码 适配器:Adapter 复制代码 1 public class Adapter implements Ps2 { 2 3 private Usb usb; 4 public Adapter(Usb usb){ 5 this.usb = usb; 6 } 7 @Override 8 public void isPs2() { 9 usb.isUsb(); 10 } 11 12 } 复制代码 测试类:Clienter 复制代码 1 public class Clienter { 2 3 public static void main(String[] args) { 4 Ps2 p = new Adapter(new Usber()); 5 p.isPs2(); 6 } 7 8 } 复制代码
2.4 结构型:代理模式
代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改
具体的静态代理和两种动态代理请看博客《静态代理和动态代理》
2.5 行为型:观察者模式
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
抽象主题(Subject)角色:即被观察者(Observable)的角色,主题的抽象类,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
具体主题(ConcreteSubject)角色:即具体被观察者(ConcreteObservable),此角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知。
抽象观察者(Observer)角色:此角色是观察者的接口类,它定义了一个更新接口,在得到主题的通知时更新自己。
具体观察者(ConcreteObserver)角色:该主题实现抽象观察者角色所定义的更新接口,以便在主题的状态发生变化时更新自身的状态。
具体实现省略,其主要意思就是当主题类(被观察者)发生改变时,就会通知所有的观察者。
2.6 行为型:策略模式
其思想是针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而是它们可以相互替换。策略模式的最大特点是使得算法可以在不影响客户端的情况下发生变化,从而改变不同的功能。