自己曾经用过一些常用的设计模式,现在主要总结一下这些设计模式的使用原理以及场景:
作为对象的创建模式,单例模式确保其某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。单例模式有以下特点:
1、单例类只能有一个实例
2、单例类必须自己创建自己的唯一实例
3、单例类必须给其他所有对象提供这一实例
下面看一下单例模式的三种写法,除了这三种写法,静态内部类的方式、静态代码块的方式、enum枚举的方式也都可以,不过异曲同工,这三种方式就不写了。
首先声明就是 在我们项目工程中 我们完全不用使用懒汉式 因为有锁使用的地方就有效率低的存在;
饿汉式
顾名思义,饿汉式,就是使用类的时候不管用的是不是类中的单例部分,都直接创建出单例类,看一下饿汉式的写法:
public class SingleEager {
public static SingleEager se = new SingleEager();
public static SingleEager getInstance()
{
return se;
}
}
这就是饿汉式单例模式的写法,也是一种比较常见的写法。这种写法会不会造成竞争,引发线程安全问题呢?答案是不会。
可能有人会觉得奇怪:第3行,CPU执行线程A,实例化一个EagerSingleton,没有实例化完,CPU就从线程A切换到线程B了,线程B此时也实例化这个EagerSingleton,然后EagerSingleton被实例化出来了两次,有两份内存地址,不就有线程安全问题了吗?
没关系,我们完全不需要担心这个问题,JDK已经帮我们想到了。Java虚拟机2:Java内存区域及对象,文中可以看一下对象创建这一部分,没有写得很详细,其实就是"虚拟机采用了CAS配上失败重试的方式保证更新更新操作的原子性和TLAB两种方式来解决这个问题"。
懒汉式
同样,顾名思义,这个人比较懒,只有当单例类用到的时候才会去创建这个单例类,看一下懒汉式的写法:
public class LazySingleton
{
private static LazySingleton instance = null;
private LazySingleton()
{
}
public static LazySingleton getInstance()
{
if (instance == null)
instance = new LazySingleton();
return instance;
}
}
这种写法基本不用,因为这是一种线程非安全的写法。试想,线程A初次调用getInstance()方法,代码走到第12行,线程此时切换到线程B,线程B走到12行,看到instance是null,就new了一个LazySingleton出来,这时切换回线程A,线程A继续走,也new了一个LazySingleton出来。这样,单例类LazySingleton在内存中就有两份引用了,这就违背了单例模式的本意了。
可能有人会想,CPU分的时间片再短也不至于getInstance()方法只执行一个判断就切换线程了吧?问题是,万一线程A调用LazySingleton.getInstance()之前已经执行过别的代码了呢,走到12行的时候刚好时间片到了,也是很正常的。
双检锁【其实这个地方叫做 带锁的双检懒汉式单利模式】
既然懒汉式是非线程安全的,那就要改进它。最直接的想法是,给getInstance方法加锁不就好了,但是我们不需要给方法全部加锁啊,只需要给方法的一部分加锁就好了。
双检的目的是为了提高效率,当第一次线程创建了实例对象后,后边进入的线程通过判断第一个是否为null,可以直接不用走入加锁的代码区;
基于这个考虑,引入了双检锁(Double Check Lock,简称DCL)的写法:
public class DoubleCheckLockSingleton
{
private static DoubleCheckLockSingleton instance = null;
private DoubleCheckLockSingleton()
{
}
public static DoubleCheckLockSingleton getInstance()
{
if (instance == null)
{
synchronized (DoubleCheckLockSingleton.class)
{
if (instance == null)
instance = new DoubleCheckLockSingleton();
}
}
return instance;
}
}
双检锁的写法是不是线程安全的呢?是的,至于为什么,不妨以分析懒汉式写法的方式分析一下双检锁的写法。
线程A初次调用DoubleCheckLockSingleton.getInstance()方法,走12行,判断instance为null,进入同步代码块,此时线程切换到线程B,线程B调用DoubleCheckLockSingleton.getInstance()方法,由于同步代码块外面的代码还是异步执行的,所以线程B走12行,判断instance为null,等待锁。结果就是线程A实例化出了一个DoubleCheckLockSingleton,释放锁,线程B获得锁进入同步代码块,判断此时instance不为null了,并不实例化DoubleCheckLockSingleton。这样,单例类就保证了在内存中只存在一份。
单例模式在Java中的应用及解读
Runtime是一个典型的例子,看下JDK API对于这个类的解释"每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接,可以通过getRuntime方法获取当前运行时。应用程序不能创建自己的Runtime类实例。",这段话,有两点很重要:
1、每个应用程序都有一个Runtime类实例
2、应用程序不能创建自己的Runtime类实例
只有一个、不能自己创建,是不是典型的单例模式?看一下,Runtime类的写法:
public class Runtime {
private static Runtime currentRuntime = new Runtime(); //使用饿汉式
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
...
}
后面的就不黏贴了,到这里已经足够了,看到Runtime使用getRuntime()方法并让构造方法私有保证程序中只有一个Runtime实例且Runtime实例不可以被用户创建。
单例模式的好处
作为一种重要的设计模式,单例模式的好处有:
1、控制资源的使用,通过线程同步来控制资源的并发访问
2、控制实例的产生,以达到节约资源的目的
3、控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信
一、简单工厂模式
public abstract class INoodles {
/**
* 描述每种面条啥样的
*/
public abstract void desc();
}
先来一份兰州拉面(具体的产品类):
public class LzNoodles extends INoodles {
@Override
public void desc() {
System.out.println("兰州拉面 上海的好贵 家里才5 6块钱一碗");
}
}
程序员加班必备也要吃泡面(具体的产品类):
public class PaoNoodles extends INoodles {
@Override
public void desc() {
System.out.println("泡面好吃 可不要贪杯");
}
}
还有我最爱吃的家乡的干扣面(具体的产品类):
public class GankouNoodles extends INoodles {
@Override
public void desc() {
System.out.println("还是家里的干扣面好吃 6块一碗");
}
}
准备工作做完了,我们来到一家“简单面馆”(简单工厂类),菜单如下:
public class SimpleNoodlesFactory {
public static final int TYPE_LZ = 1;//兰州拉面
public static final int TYPE_PM = 2;//泡面
public static final int TYPE_GK = 3;//干扣面
public static INoodles createNoodles(int type) {
switch (type) {
case TYPE_LZ:
return new LzNoodles();
case TYPE_PM:
return new PaoNoodles();
case TYPE_GK:
default:
return new GankouNoodles();
}
}
}
1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。
2 create()方法通常是静态的,所以也称之为静态工厂。
1 扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)
2 不同的产品需要不同额外参数的时候 不支持。
package com.demoFound.factoryMethod.factory;
import com.demoFound.factoryMethod.message.IMyMessage;
/**
* 工厂方法模式_工厂接口
* @author popkidorc
*/
public interface IMyMessageFactory {
public IMyMessage createMessage(String messageType);
}
/**
* 工厂方法模式_工厂实现
* @author popkidorc
*/
public class MyMessageFactory implements IMyMessageFactory {
@Override
public IMyMessage createMessage(String messageType) {
// 这里的方式是:消费者知道自己想要什么产品;若生产何种产品完全由工厂决定,则这里不应该传入控制生产的参数。
IMyMessage myMessage;
Map<String, Object> messageParam = new HashMap<String, Object>();
// 根据某些条件去选择究竟创建哪一个具体的实现对象,条件可以传入的,也可以从其它途径获取。
// sms
if ("SMS".equals(messageType)) {
myMessage = new MyMessageSms();
messageParam.put("PHONENUM", "123456789");
} else
// OA待办
if ("OA".equals(messageType)) {
myMessage = new MyMessageOaTodo();
messageParam.put("OAUSERNAME", "testUser");
} else
// email
if ("EMAIL".equals(messageType)) {
myMessage = new MyMessageEmail();
messageParam.put("EMAIL", "test@test.com");
} else
// 默认生产email这个产品
{
myMessage = new MyMessageEmail();
messageParam.put("EMAIL", "test@test.com");
}
myMessage.setMessageParam(messageParam);
return myMessage;
}
}
产品:
package com.demoFound.factoryMethod.message;
import java.util.Map;
/**
* 工厂方法模式_产品接口
* @author popkidorc
*/
public interface IMyMessage {
public Map<String, Object> getMessageParam();
public void setMessageParam(Map<String, Object> messageParam);
public void sendMesage() throws Exception;// 发送通知/消息
}
package com.demoFound.factoryMethod.message;
import java.util.Map;
/**
* 工厂方法模式_虚拟产品类
* @author popkidorc
*/
public abstract class MyAbstractMessage implements IMyMessage {
private Map<String, Object> messageParam;// 这里可以理解为生产产品所需要的原材料库。最好是个自定义的对象,这里为了不引起误解使用Map。
@Override
public Map<String, Object> getMessageParam() {
return messageParam;
}
@Override
public void setMessageParam(Map<String, Object> messageParam) {
this.messageParam = messageParam;
}
}
package com.demoFound.factoryMethod.message;
/**
* 工厂方法模式_email产品
* @author popkidorc
*/
public class MyMessageEmail extends MyAbstractMessage {
@Override
public void sendMesage() throws Exception {
// TODO Auto-generated method stub
if (null == getMessageParam() || null == getMessageParam().get("EMAIL")
|| "".equals(getMessageParam().get("EMAIL"))) {
throw new Exception("发送短信,需要传入EMAIL参数");// 为了简单起见异常也不自定义了
}// 另外邮件内容,以及其他各种协议参数等等都要处理
System.out.println("我是邮件,发送通知给" + getMessageParam().get("EMAIL"));
}
}
package com.demoFound.factoryMethod.message;
/**
* 工厂方法模式_oa待办产品
* @author popkidorc
*/
public class MyMessageOaTodo extends MyAbstractMessage {
@Override
public void sendMesage() throws Exception {
// TODO Auto-generated method stub
if (null == getMessageParam()
|| null == getMessageParam().get("OAUSERNAME")
|| "".equals(getMessageParam().get("OAUSERNAME"))) {
throw new Exception("发送OA待办,需要传入OAUSERNAME参数");// 为了简单起见异常也不自定义了
}// 这里的参数需求就比较多了不一一处理了
System.out
.println("我是OA待办,发送通知给" + getMessageParam().get("OAUSERNAME"));
}
}
package com.demoFound.factoryMethod.message;
/**
* 工厂方法模式_sms产品
* @author popkidorc
*/
public class MyMessageSms extends MyAbstractMessage {
@Override
public void sendMesage() throws Exception {
// TODO Auto-generated method stub
if (null == getMessageParam()
|| null == getMessageParam().get("PHONENUM")
|| "".equals(getMessageParam().get("PHONENUM"))) {
throw new Exception("发送短信,需要传入PHONENUM参数");// 为了简单起见异常也不自定义了
}// 另外短信信息,以及其他各种协议参数等等都要处理
System.out.println("我是短信,发送通知给" + getMessageParam().get("PHONENUM"));
}
}
消费者:
package com.demoFound.factoryMethod;
import com.demoFound.factoryMethod.factory.IMyMessageFactory;
import com.demoFound.factoryMethod.factory.MyMessageFactory;
import com.demoFound.factoryMethod.message.IMyMessage;
/**
* 工厂方法模式_消费者类
* @author popkidorc
*/
public class MyFactoryMethodMain {
public static void main(String[] args) {
IMyMessageFactory myMessageFactory = new MyMessageFactory();
IMyMessage myMessage;
// 对于这个消费者来说,不用知道如何生产message这个产品,耦合度降低
try {
// 先来一个短信通知
myMessage = myMessageFactory.createMessage("SMS");
myMessage.sendMesage();
// 来一个oa待办
myMessage = myMessageFactory.createMessage("OA");
myMessage.sendMesage();
// 来一个邮件通知
myMessage = myMessageFactory.createMessage("EMAIL");
myMessage.sendMesage();
} catch (Exception e) {
e.printStackTrace();
}
}
}
抽象工厂模式代码
interface IProduct1 {
public void show();
}
interface IProduct2 {
public void show();
}
class Product1 implements IProduct1 {
public void show() {
System.out.println("这是1型产品");
}
}
class Product2 implements IProduct2 {
public void show() {
System.out.println("这是2型产品");
}
}
interface IFactory {
public IProduct1 createProduct1();
public IProduct2 createProduct2();
}
class Factory implements IFactory{
public IProduct1 createProduct1() {
return new Product1();
}
public IProduct2 createProduct2() {
return new Product2();
}
}
public class Client {
public static void main(String[] args){
IFactory factory = new Factory();
factory.createProduct1().show();
factory.createProduct2().show();
}
}
抽象工厂模式的优点
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
抽象工厂模式的缺点
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
1、定义一个抽象被观察者接口
/***
* 被观察者接口
* 声明了添加、删除、通知观察者方法
* @author jstao
*/
public interface ICompile{
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
2、定义一个抽象观察者接口
/***
* 抽象观察者
* 定义了一个update()方法,当被观察者调用notifyObservers()方法时,观察者的update()方法会被回调。
* @author jstao
*/
public interface ITaskRun{
public void run(String message);
}
3、定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。
/**
* 被观察者,也就是微信公众号服务
* 实现了Observerable接口,对Observerable接口的三个方法进行了具体实现
* @author jstao
*/
public class WechatServer implements ICompile{
//注意到这个List集合的泛型参数为Observer接口,设计原则:面向接口编程而不是面向实现编程
private List<ICompile> list;
private String message;
public WechatServer() {
list = new ArrayList<ICompile>();
}
@Override
public void registerObserver(ICompile o) {
list.add(o);
}
@Override
public void removeObserver(ICompile o) {
if(!list.isEmpty())
list.remove(o);
}
//遍历
@Override
public void notifyObserver() {
for(int i = 0; i < list.size(); i++) {
ICompile com = list.get(i);
com.update(message);
}
}
public void setInfomation(String s) {
this.message = s;
System.out.println("微信服务更新消息: " + s);
//消息更新,通知所有观察者
notifyObserver();
}
}
4、定义具体观察者,微信公众号的具体观察者为用户User
/**
* 观察者
* 实现了update方法
* @author jstao
*
*/
public class User implements ITaskRun{
private String name;
private String message;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
this.message = message;
read();
}
public void read() {
System.out.println(name + " 收到推送消息: " + message);
}
}