目录
1. 为什么需要学习设计模式
设计模式(Design pattern)代表了最佳的实践,是很多优秀的软件开发人员的经验总结,是解决特定问题的解决方案。它并不是语法规定,也不拘泥于特定语言。 恰当的使用设计模式可以代码的可复用性,可维护性,可扩展性,健壮性及安全性,这些都是系统非常重要的非功能性需求。
设计模式的广泛使用起始于1995年,GOF(四人帮)出版的《设计模式:可复用面向对象软件基础》。
本部分相关的示例代码:
https://gitee.com/lisenaq/patterndemo.git
二、常见设置模式
2.1 单例模式
2.1.1 概念
保证在内存中只用一个实例
2.1.2 使用场景
比如:系统配置文件的管理,这些配置文件只要使用一个单例对象进行读写即可,系统总其他地方需要使用配置信息时,只要使用该单例对象进行获取就可以了,这样便于统一管理配置信息。
2.1.3 优缺点
🟡优点:
- 在内存中只有一个对象,节省内存空间;
- 避免频繁的创建销毁对象,可以提高性能;
- 避免对共享资源的多重占用,简化访问;
- 为整个系统提供一个全局访问点。
🟣缺点:
- 不适用于变化频繁的对象;
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
2.1.4 示例
1.饥饿模式:直接实例化对象
package org.lisen.patterndemo.singleton;
/**
* 单例模式,饥饿加载
*/
public class SingletonDemo01 {
//1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
private SingletonDemo01(){}
//2. 饥饿模式,首先生成一个实例
private static final SingletonDemo01 instance = new SingletonDemo01();
//3. 静态方法,用于获取已经生成的实例
public static SingletonDemo01 getInstance() {
return instance;
}
public String hello(String name) {
return "hello " + name;
}
/*
* 测试多线程下单例模式是否安全
* @param args
*/
public static void main(String[] args) {
/*for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(SingletonDemo01.getInstance().hashCode());
}).start();
}*/
SingletonDemo01 s = SingletonDemo01.getInstance();
String hello_world = s.hello("hello world");
System.out.println(hello_world);
}
}
这种直线方式简单,且是线程安全的。
2.懒汉模式:调用方法时进行实例化对象。
- 第一种写法:
/**
* 单例模式: 懒汉式
*/
public class SingletonDemo02 {
private SingletonDemo02(){
//模拟构造函数的运行耗时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static SingletonDemo02 singletonDemo02 = null;
public static SingletonDemo02 getInstance() {
if (singletonDemo02 == null) {
singletonDemo02 = new SingletonDemo02();
}
return singletonDemo02;
}
public String hello(String name) {
return "hello " + name;
}
}
注意: 这种方式在多线程访问时会有问题。
- 第二种写法:
在上一个方法的基础上增加了同步锁synchronized
package org.lisen.patterndemo.singleton;
/**
* 单例模式: 懒汉式, 有线程问题
*/
public class SingletonDemo03 {
private SingletonDemo03(){
//模拟构造函数的运行耗时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static SingletonDemo03 singletonDemo02 = null;
public static synchronized SingletonDemo03 getInstance() {
if (singletonDemo03 == null) {
singletonDemo03 = new SingletonDemo03();
}
return singletonDemo03;
}
public String hello(String name) {
return "hello " + name;
}
/*
* 测试多线程下单例模式是否安全
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(SingletonDemo03.getInstance().hashCode());
}).start();
}
}
}
- 第三种写法
public class SingletonDemo04 {
private SingletonDemo04() {
}
private static SingletonDemo04 singletonDemo04 = null;
public static SingletonDemo04 getInstance(){
//系统减小同步块来提升性能,可以吗?
if(singletonDemo04 == null) {
synchronized (SingletonDemo04.class) {
singletonDemo04 = new SingletonDemo04();
}
}
return singletonDemo04;
}
public String hello(String name) {
return "hello " + name;
}
}
该方式依然会有线程安全问题
- 第四种写法
在上个方法基础上进行双重检查 首先判断是否为空 如果为空则加上同步锁 之后再 第二次判断
package org.lisen.patterndemo.singleton;
/**
* 单例模式: 懒汉式,线程安全,但性能较低
*/
public class SingletonDemo05 {
private SingletonDemo05() {
//模拟构造函数的运行耗时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static SingletonDemo05 singletonDemo05 = null;
public static SingletonDemo05 getInstance(){
//系统减小同步块来提升性能,可以吗?
if(singletonDemo05 == null) {
///....
synchronized (SingletonDemo05.class) {
if(singletonDemo05 == null) {
singletonDemo05 = new SingletonDemo05();
}
}
}
return singletonDemo05;
}
public String hello(String name) {
return "hello " + name;
}
/*
* 测试多线程下单例模式是否安全
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(SingletonDemo05.getInstance().hashCode());
}).start();
}
}
}
- 第五种写法
写一个静态的内部类 在静态的内部类里面声明一个静态的实例(是一个单线程没有线程安全问题)
package org.lisen.patterndemo.singleton;
/**
* 单例模式: 懒加载, 线程安全
*/
public class SingletonDemo04 {
//阻止外部实例化
private SingletonDemo04() {
//模拟构造函数运行耗时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static {
}
//使用静态内部类来使用一个SingletonDemo04对象
private static class SingletonDemoHolder {
private final static SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonDemoHolder.instance;
}
public String hello(String name) {
return "hello " + name;
}
/*
* 测试多线程下单例模式是否安全
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(SingletonDemo04.getInstance().hashCode());
}).start();
}
}
}
- 第六种写法
enum 枚举型(没有构造函数) 可以保证单例,且线程安全
package org.lisen.patterndemo.singleton;
public enum SingletonDemo05 {
INSTANCE;
public String hello(String name) {
return "hello " + name;
}
/*
* 测试多线程下单例模式是否安全
* @param args
*/
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(SingletonDemo05.INSTANCE.hashCode());
}).start();
}
}
}
注意:单例模式既要保证懒加载也要保证线程安全
2.2 工厂模式
2.2.1 概念
用于产生对象的方法或者式类,称之为工厂。 上面所讲到的单例模式也可以看作为一个特殊的工厂。
2.2.2 使用场景
❓为什么需要工作模式,原来使用new的方式感觉也很简单,且好懂?
🔍使用工厂的原因是我们可以通过工厂模式,来集中控制对象的创建过程,这样可以给设计带来更多的灵活性。比如:spring的IOC容器就是工厂模式的经典实现。
2.2.3 工厂方法
用于生产指定系列的对象。已鸭子为例,鸭子有真的鸭子,橡皮鸭,电子玩具鸭等。如何能方便的创建出各种鸭子,并将创建过程控制起来,以便于以后的维护和扩展?
类图:
抽象鸭子父类Duck
public abstract class Duck {
abstract public void quack();
}
RubberDuck鸭子子类,继承Duck抽象父类
public class RubberDuck extends Duck {
@Override
public void quack() {
System.out.println("我是橡皮鸭,");
}
}
WildDuck鸭子子类,继承Duck抽象父类
public class WildDuck extends Duck {
@Override
public void quack() {
System.out.println("我是真鸭子");
}
}
DonaldDuck鸭子子类,继承Duck抽象父类
public class DonaldDuck extends Duck {
@Override
public void quack() {
System.out.println("我是唐老鸭");
}
}
编写控制鸭子类创建的工厂DuckFactory
public class DuckFactory {
//私有化构造方法
private DuckFactory(){}
//static 饿汉模式实例出一个类型为鸭子工厂类的对象
private static DuckFactory duckFactory = new DuckFactory();
//定义图中的几种子类的鸭子类,类型用数字区分,可以手动增加
public static final int WILD_DUCK = 1;
public static final int RUBBER_DUCK = 2;
public static final int DONALD_DUCK = 3;
public static final int PJ_DUCK = 4;
//依据鸭子类型得到鸭子实例的方法
public static Duck getInstance(int duckType) {
switch (duckType) {
case WILD_DUCK:
//返回直接实例化好的鸭子子类
return new WildDuck();
case RUBBER_DUCK:
return new RubberDuck();
case DONALD_DUCK:
return new DonaldDuck();
case PJ_DUCK:
return new PJDuck();
default:
return null;
}
}
}
测试
public class Main {
public static void main(String[] args) {
//DuckFactory可以生产鸭子对象
Duck donaldDuck = DuckFactory.getInstance(DuckFactory.DONALD_DUCK);
donaldDuck.quack();
Duck wildDuck = DuckFactory.getInstance(DuckFactory.WILD_DUCK);
wildDuck.quack();
Duck pjDuck = DuckFactory.getInstance(DuckFactory.PJ_DUCK);
pjDuck.quack();
}
}
以上就是关于常见设置模式01的内容📖