Java设计模式(更新中。。。)
1、单例模式(Singleton)
-
如何实现单例?
一般我们获取实例是直接通过new实现,原因是类中默认有无参构造方法。但是通过new来创建实例则可以创建多个实例,而单例模式是只创建一个实例。因此,我们设置构造方法私有化(private)这样其他类就不能直接通过构造方法创建该实例
-
应用场合
在程序中有些对象它只需要一个就足够了,不需要创建多个对象(实例)时,可以使用单例模式,这样可以保证整个应用程序中有且只有一个该对象(实例)。如:日志文件,线程池,数据库连接池等。
1.1 饿汉式单例
-
什么是饿汉模式?
当类加载的时候创建其唯一实例(你可以理解为太饿了马上就要吃饱,所以类一旦加载就马上创建实例)
-
代码:
/** * @program: soft_test * @description: 单例模式-饿汉式 * @author: Mr.zuo * @create: 2021-02-17 22:18 **/ public class HungrySingleton { /** * 1.将构造方法私有化 * 目的:不允许外部类之间创建对象(new) * */ private HungrySingleton(){ } /** *2. 在单例类的内部创建对象,并且static话, * 目的: static后可以让该类加载后直接创建该类的实例对象 * */ private static HungrySingleton instance = new HungrySingleton(); /** * 3.提供一个获取实例的方法 * 目的: 给实例private后,为了让外部获取实例,提供一个public的方法供外部调用获取实例 * */ public static HungrySingleton getInstance(){ return instance; } } //=======================================测试================================================== /** * @program: soft_test * @description: 饿汉式单例测试 * @author: Mr.zuo * @create: 2021-02-17 22:33 **/ public class HungrySingletonTest { public static void main(String[] args) { HungrySingleton singleton1 = HungrySingleton.getInstance(); HungrySingleton singleton2 = HungrySingleton.getInstance(); if (singleton1 == singleton2){ System.out.println("为同一个实例对象");// true }else { System.out.println("为不同实例对象");// false } } } -
特点
-
线程安全
-
类加载的同时会创建对象,此时相对懒汉式速度慢点,但是调用该对象的时候相对更快
-
1.2 懒汉式单例
-
什么是懒汉式单例?
当类加载时不会立即创建该类的实例对象,当需要调用该对象时才会创建(你可以理解为我很懒;我有拖延症。我创建类的对象的时候并不想第一时间实例化,只有等到要用到的时候才实例化)
-
代码
/** * @program: soft_test * @description: 单例模式之懒汉式 * @author: Mr.zuo * @create: 2021-02-17 22:53 **/ public class LazySingleton { /** * 1.将构造方法私有化,避免外界类直接创建对象 * */ private LazySingleton(){} /** * 2.使用private,static修饰,但是不会直接创建该实例 * */ private static LazySingleton instance; /** * 3.提供调用该实例的方法,当第一次获取该实例的时候,创建该实例,否则直接返回该实例 * */ public static LazySingleton getInstance(){ if (instance == null){ instance = new LazySingleton(); } return instance; } } //=======================================测试================================================== /** * @program: soft_test * @description: 懒汉式单例测试 * @author: Mr.zuo * @create: 2021-02-17 22:58 **/ public class LazySingletonTest { public static void main(String[] args) { LazySingleton singleton1 = LazySingleton.getInstance(); LazySingleton singleton2 = LazySingleton.getInstance(); if (singleton1 == singleton2){ System.out.println("为同一实例");// true }else { System.out.println("为不同实例");// false } } } -
特点
1、线程不安全
2、加载类时速度相对饿汉式更快,但是调用该类时速度相对就慢点
1.3双重检查模式---推荐(最常用)
-
什么是双重检查模式?
当外部类需要创建该类的实例时,最多需要进行双重检查。
-
为什么要进行双重检查?
采用双重检查模式,既可以保证线程安全,又可以做到延迟初始化(即与懒汉模式一样),同时性能方面得到优化
-
代码
/** * @program: soft_test * @description: 双重检查模式 * @author: Mr.zuo * @create: 2021-02-17 23:11 **/ public class DoubleCheckSingleton { /** * 构造方法私有化 * */ private DoubleCheckSingleton(){} /** * volatile修饰: 禁止指令重排 * */ private volatile static DoubleCheckSingleton instance; public static DoubleCheckSingleton getInstance(){ //减少不要同步,优化性能 if (instance == null){ // 同步,保证线程安全 synchronized (DoubleCheckSingleton.class){ if (instance == null){ // 创建实例 instance = new DoubleCheckSingleton(); } } } return instance; } } //=======================================测试================================================== /** * @program: soft_test * @description: 双重检查单例模式测试 * @author: Mr.zuo * @create: 2021-02-17 23:16 **/ public class DoubleCheckSingletonTest { public static void main(String[] args) { DoubleCheckSingleton singleton1 = DoubleCheckSingleton.getInstance(); DoubleCheckSingleton singleton2 = DoubleCheckSingleton.getInstance(); if (singleton1 == singleton2){ System.out.println("实例对象相同");// true }else { System.out.println("实例对象不同");//false } } } -
特点
1、延迟初始化。和懒汉模式一致,只有在初次调用静态方法
getSingleton,才会初始化signleton实例。2、性能优化。同步会造成性能下降,在同步前通过判读
singleton是否初始化,减少不必要的同步开销。3、线程安全。同步创建Singleton对象,同时注意到静态变量
singleton使用volatile修饰。
1.4静态内部类模式--推荐
-
什么是静态内部类模式?
采用静态内部类的方法创建单例模式
-
为什么要使用静态内部类模式?
代码简洁,保证线程安全,且延迟初始化
-
代码
/** * @program: soft_test * @description: 静态内部类模式 * @author: Mr.zuo * @create: 2021-02-17 23:35 **/ public class StaticSingleton { private StaticSingleton(){} public static StaticSingleton getInstance(){ return Inner.instance; } public static class Inner{ private static final StaticSingleton instance = new StaticSingleton(); } } //=======================================测试==================================================// /** * @program: soft_test * @description: 静态内部类模式测试 * @author: Mr.zuo * @create: 2021-02-17 23:37 **/ public class StaticSingletonTest { public static void main(String[] args) { StaticSingleton singleton1 = StaticSingleton.getInstance(); StaticSingleton singleton2 = StaticSingleton.getInstance(); if (singleton1 == singleton2){ System.out.println("为同一实例");// true }else { System.out.println("为不同实例");// false } } } -
特点
1、实现代码简洁。和双重检查单例对比,静态内部类单例实现代码真的是太简洁,又清晰明了。
2、延迟初始化。调用
getSingleton才初始化Singleton对象。3、线程安全。JVM在执行类的初始化阶段,会获得一个可以同步多个线程对同一个类的初始化的锁。
参考:
3、单例模式面试问题
2、工厂模式(factory)
2.1 什么是工厂模式?
工厂顾名思义就是创建产品的场所,这里的产品类比Java中的对象。一般情况我们创建对象是通过new关键字创建 ,而使用工厂模式则是将对象交给工厂创建,我们直接从工厂中获取所需产品的对象即可。工厂模式又分为简单工厂模式(又称静态工厂模式),方法工厂模式以及抽象工厂模式。
2.2 在Java中的应用场景
- Mybatis中SqlSessionFactory、ObjectFactory、MapperProxyFactory的创建
- Spring中BeanFactory的创建用于管理Bean的工厂
- Jdk中Collection中的iterator方法
2.3 简单工厂模式(静态工厂模式)
2.3.1概括
简单工厂实际不能算作一种设计模式,它引入了创建者的概念,将实例化的代码从应用代码中抽离,在创建者类的静态方法中只处理创建对象的细节,后续创建的实例如需改变,只需改造创建者类(即工厂类)即可,不过,但由于使用静态方法来获取对象,使其不能在运行期间通过不同方式去动态改变创建行为,因此存在一定局限性
首先我们以造车为例子,假设你要买车,但是车有好多种,你不知道买哪种,你可以去造车得工厂看是否有你需要得品牌得车,而创建车得实例直接交由工厂去做即可(这个例子可能举的不是很合适,但是意思差不多)。
2.3.2 代码如下
- 1.构造车的抽象类
/**
* @program: soft_test
* @description: 宝马,奔驰,玛莎拉蒂等均是车的具体实例,而车只是一个抽象概念。
* @author: Mr.zuo
* @create: 2021-02-24 17:31
**/
public abstract class Car {
/**
* 车的名字
* */
abstract String name();
}
*2.创建具体车的实例
/**
* @program: soft_test
* @description: 车-奔驰
* @author: Mr.zuo
* @create: 2021-02-24 17:38
**/
public class Benz extends Car{
@Override
String name() {
String Benz = "奔驰";
return Benz;
}
}
/**
* @program: soft_test
* @description: 车-宝马
* @author: Mr.zuo
* @create: 2021-02-24 17:32
**/
public class BMW extends Car{
@Override
String name() {
String BMW="宝马";
return BMW;
}
}
- 3.创建车的工厂
/**
* @program: soft_test
* @description: 创建车的工厂
* @author: Mr.zuo
* @create: 2021-02-24 17:47
**/
public class CarFactory {
private static String BMW = "宝马";
private static String Benz = "奔驰";
public static Car getCar(String car) {
if (BMW.equals(car)) {
return new BMW();
} else if (Benz.equals(car)) {
return new Benz();
} else {
return null;
}
}
}
从CarFactory发现,当有新的车的实例需要添加时,我们需要修改CarFactory发现的代码,这不符合开闭原则。
- 4.创建简单工厂模式
/**
* @program: soft_test
* @description: 简单工厂模式(静态工厂模式)-用来生产同一级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
* 本质:实力化对象的时候不使用new创建,而使用工厂方法创建
* 并将选择实现类,创建对象统一管理和控制。从而将调用者和实现类解耦
* 缺点:不满足开闭原则,增加新的对象需要修改CarFactory中的代码
* @author: Mr.zuo
* @create: 2021-02-24 17:28
**/
public class SimpleFactory {
public static void main(String[] args) {
/**
* 使用工厂创建-使用CarFactory实例化,不用new
* 传统方法: Benz benz = new Benz();
* benz.name;
* */
Car c1 = CarFactory.getCar("宝马");
Car c2 = CarFactory.getCar("奔驰");
System.out.println(c1.name());
System.out.println(c2.name());
}
}
2.3.3 简单工厂模式的优点
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
2.3.4 简单工厂模式的缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
2.3.5 适用场景
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
2.4 工厂方法模式
2.4.1 前言
根据上文的简单工厂模式我们可以了解到其缺点就是不遵守闭开原则,由此提出了工厂方法模式。每个抽象Car类的实例都对应自己的工厂,由自己的工厂创建该产品Car的实例,再统一拿到CarFactory,用户只需从CarFactory拿到类的的实例即可。
2.4.2 代码如下
注: 车的实例以及抽象车的类是不变,跟上文一致,这里就不复用。
- 抽象类car的工厂类
/**
* @program: soft_test
* @description:
* @author: Mr.zuo
* @create: 2021-02-24 18:10
**/
public abstract class CarFactory {
/**
* 获取具体车的方法,为什么返回的是Car类型的数组呢?
* 就比如宝马系列车来看,有宝马X6.宝马X7,
* 宝马跑车等不同款式,这一个个款式都由制造宝马的工厂创建。
* */
abstract Car[] getCar();
}
- 具体类Benz,BWM的工厂类
/**
* @program: soft_test
* @description: 创建奔驰车的工厂
* @author: Mr.zuo
* @create: 2021-02-24 18:11
**/
public class BenzFactory extends CarFactory{
@Override
public Car[] getCar() {
return new Car[]{new Benz()};
}
}
/**
* @program: soft_test
* @description: 宝马工厂-创建宝马车
* @author: Mr.zuo
* @create: 2021-02-24 18:47
**/
public class BMWFactory extends CarFactory{
@Override
public Car[] getCar() {
return new Car[]{new BMW()};
}
}
- 创建工厂方法模式
/**
* @program: soft_test
* @description: 工厂方法模式-定义一个创建对象的接口,
* 但由子类去决定实例化的类是哪个,工厂方法让类的实例化交给子类
* @author: Mr.zuo
* @create: 2021-02-24 17:28
**/
public class SimpleFactory {
public static void main(String[] args) {
Car[] car1 = new BenzFactory().getCar();
for (Car car : car1) {
System.out.println(car.name());
}
Car[] car2 = new BMWFactory().getCar();
for (Car car : car2) {
System.out.println(car.name());
}
}
}
2.4.3 工厂方法模式的优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
2.4.4 工厂方法模式的缺点
- 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
2.4.5 适用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
2.5 抽象工厂模式
2.5.1 概括
举个栗子,小米公司和华为公司做手机的还有做路由器等其他。其中小米工厂负责管理生产手机和路由器的小米工厂,小米手机工厂则负责生产小米手机,小米路由器工厂则负责生产小米路由器。所以这里引出一个概念产品家族,在此例子中,不同的产品就组成我们的抽象产品工厂, 抽象产品工厂开始承担创建者的责任,负责制造不同的产品。
2.5.2 代码如下
- 手机和路由器产品接口
/**
* @program: soft_test
* @description: 手机产品接口
* @author: Mr.zuo
* @create: 2021-02-24 23:05
**/
public interface IphoneProduct {
void startPhone();
void shutdown();
void callup();
void sendSMS();
}
/**
* @program: soft_test
* @description: 路由器产品接口
* @author: Mr.zuo
* @create: 2021-02-24 23:07
**/
public interface IRouterProduct {
void startRouter();
void shutdown();
void openwifi();
void setting();
}
- 手机和路由器的具体实现类
/**
* @program: soft_test
* @description: 小米手机
* @author: Mr.zuo
* @create: 2021-02-24 23:09
**/
public class XiaomiPhone implements IphoneProduct{
@Override
public void startPhone() {
System.out.println("小米开机");
}
@Override
public void shutdown() {
System.out.println("小米关机");
}
@Override
public void callup() {
System.out.println("小米打电话");
}
@Override
public void sendSMS() {
System.out.println("小米发短信");
}
}
/**
* @program: soft_test
* @description: 华为手机
* @author: Mr.zuo
* @create: 2021-02-24 23:10
**/
public class HuaweiPhone implements IphoneProduct{
@Override
public void startPhone() {
System.out.println("华为开机");
}
@Override
public void shutdown() {
System.out.println("华为关机");
}
@Override
public void callup() {
System.out.println("华为打电话");
}
@Override
public void sendSMS() {
System.out.println("华为发短信");
}
}
/**
* @program: soft_test
* @description: 小米路由器
* @author: Mr.zuo
* @create: 2021-02-24 23:11
**/
public class XiaomiRouter implements IRouterProduct{
@Override
public void startRouter() {
System.out.println("小米路由器开机");
}
@Override
public void shutdown() {
System.out.println("小米路由器关机");
}
@Override
public void openwifi() {
System.out.println("小米路由器打开wifi");
}
@Override
public void setting() {
System.out.println("小米路由器设置");
}
}
/**
* @program: soft_test
* @description: 华为路由器
* @author: Mr.zuo
* @create: 2021-02-24 23:12
**/
public class HuaweiRouter implements IRouterProduct{
@Override
public void startRouter() {
System.out.println("华为路由器开机");
}
@Override
public void shutdown() {
System.out.println("华为路由器关机");
}
@Override
public void openwifi() {
System.out.println("华为路由器打开wifi");
}
@Override
public void setting() {
System.out.println("华为路由器设置");
}
}
- 抽象产品工厂售卖手机和路由器(生产由其具体工厂生产)
/**
* @program: soft_test
* @description: 抽象产品工厂
* @author: Mr.zuo
* @create: 2021-02-24 23:15
**/
public interface AbstractProductFactory {
// 生产手机
IphoneProduct phoneProduct();
// 生产路由器
IRouterProduct routerProduct();
}
- 生产产品的具体工厂
/**
* @program: soft_test
* @description: 小米工厂-用于生产小米的产品
* @author: Mr.zuo
* @create: 2021-02-24 23:17
**/
public class XiaomiFactory implements AbstractProductFactory{
@Override
public IphoneProduct phoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new XiaomiRouter();
}
}
/**
* @program: soft_test
* @description: 华为工厂-用于生产华为产品
* @author: Mr.zuo
* @create: 2021-02-24 23:17
**/
public class HuaweiFactory implements AbstractProductFactory{
@Override
public IphoneProduct phoneProduct() {
return new HuaweiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new HuaweiRouter();
}
}
- 创建抽象工厂模式
/**
* @program: soft_test
* @description:
* @author: Mr.zuo
* @create: 2021-02-24 23:19
**/
public class AbstractFactory {
public static void main(String[] args) {
System.out.println("====小米产品====");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct phoneProduct = xiaomiFactory.phoneProduct();
phoneProduct.callup();
phoneProduct.sendSMS();
IRouterProduct routerProduct = xiaomiFactory.routerProduct();
routerProduct.openwifi();
routerProduct.setting();
System.out.println("====华为产品====");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct iphoneProduct = huaweiFactory.phoneProduct();
iphoneProduct.sendSMS();
huaweiFactory.routerProduct().openwifi();
}
}
2.5.3 抽象工厂的优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
2.5.4 抽象工厂的缺点
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
2.5.5 适用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
注: 对于抽象工厂模式,个人感觉确实相对其他两个更难理解(反正我是看第一遍没看懂),不过好在我下面参考的文章给了我很多帮助,让我了解了这工厂模式,对于抽象工厂模式个人感悟确实不深因此很少加入个人理解,采用的基本上是参考文章里更容易让我理解的内容,我承认这种方法对个人提升其实不大,对读者也是不负责任的表现,但是写抽象工厂模式时真的心理上太痛苦了加上身体原因希望看到这篇文章的读者可以理解,我会再接再厉,共勉。 参考: