来源:https://www.bilibili.com/video/BV1mc411h719?from=search&seid=16647315943328944616
哔哩哔哩:狂神说
菜鸟教程设计模式
面试必备:常用的设计模式总结
设计模式的七大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
继承时候,尽量添加新的方法,不要改变父类原有的功能,重写父类方法,会使继承体系的可复用性变差。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
创建型模型:单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式
单例模式:
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
饿汉式单例:
直接使用final,多线程安全,可能会造成空间浪费,是否 Lazy 初始化:否
package danlimoshi;
//饿汉式单例
//单例模式构造器私有
public class Hungry {
// 饿汉模式,先将下面的对象加载进内存,可能会浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getHungry() {
return HUNGRY;
}
}
懒汉式单例:
懒汉式多线程不安全的版本,懒加载:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
为什么多线程不安全呢?
lazyMan = new LazyMan();
/**
* new LazyMan()不是原子操作,执行过程中会执行下面三步操作,并且执行顺序不一定,比如执行顺序为1,3,2,执行完1,3后,一个线程抢占了资源,此时实例对象不是null,但是它也确实是没有完成实例化,会出问题。
* 1、虚拟机为对象分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
懒汉式多线程安全的版本懒加载 使用同步返回实例对象的方法:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式多线程安全,并且能够防止一部分反射破坏的版本。懒加载
例子中包含了解决多线程不安全的办法:双重检测锁模式的懒汉单例,DCL懒汉模式,还包含了如何使用标志位“密码”来防止用户使用反射创建多个实例对象。但是用户还可以通过反射修改标志位“密码”,还是可以创建多个单例。
package danlimoshi;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
//懒汉式单例模式
//https://www.bilibili.com/video/BV1K54y197iS
public class LazyMan {
// Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,
// volatile 是因为其本身包含“禁止指令重排序”的语义,
// synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,
// 此规则决定了持有同一个对象锁的两个同步块只能串行执行。
private volatile static LazyMan lazyMan;
// 一个防止别人使用反射破坏的“密码”,名字很重要,名字暴露之后,可以通过反射修改这个“密码”继续破坏,还是能生成多个实例对象
private static boolean jiamihouodebiaoqian = false;
private LazyMan(){
synchronized (LazyMan.class){
if(jiamihouodebiaoqian == false){
jiamihouodebiaoqian = true;
}
else{
throw new RuntimeException("不要试图使用反射");
}
// 防止使用getLazyMan后再使用反射
// if(lazyMan!=null){
// throw new RuntimeException("不要试图使用反射");
// }
}
}
// 双重检测锁模式的懒汉单例,DCL懒汉模式
public static LazyMan getLazyMan(){
// 此处判断是为提升性能
if (lazyMan ==null){
synchronized (LazyMan.class){
if (lazyMan ==null) {
lazyMan = new LazyMan();
/**
* new LazyMan()不是原子操作
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*/
}
}
}
return lazyMan;
}
// 单线程下没有问题,多线程就有问题了
// public static void main(String[] args){
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// LazyMan.getLazyMan();
// }).start();
// }
// }
public static void main(String[] args) throws Exception {
// LazyMan lazyMan = LazyMan.getLazyMan();
// LazyMan lazyMan2 = LazyMan.getLazyMan();
// System.out.println(lazyMan==lazyMan2);//true
//破坏加密flag:jiamihouodebiaoqian
Field jiamihouodebiaoqian = LazyMan.class.getDeclaredField("jiamihouodebiaoqian");
//不检查私有的变量,让反射机制可以访问私有变量
jiamihouodebiaoqian.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 无视私有构造器
declaredConstructor.setAccessible(true);
LazyMan lazyMan1 = declaredConstructor.newInstance();
//获取第一个实例对象后,修改“密码”标志位,再次调用反射
//通过反射写入属性
jiamihouodebiaoqian.set(lazyMan1,false);
LazyMan lazyMan3 = declaredConstructor.newInstance();
System.out.println(lazyMan1==lazyMan3);
}
}
双检锁/双重校验锁(DCL,即 double-checked locking)
多线程安全,并且多次判断是否为null,能够保持性能,懒加载
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;
}
}
登记式/静态内部类
多线程安全,并且懒加载
package danlimoshi;
//静态内部类
//只要是单例模式就要构造器私有
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Holder {
private Holder(){
}
public static class Innerclass{
private static final Holder HOLDER = new Holder();
}
public static Holder getInstance(){
// ,反射就给破解了
return Innerclass.HOLDER;
}
}
枚举
枚举不会被反射破坏:
package danlimoshi;
import java.lang.reflect.Constructor;
//枚举本身是一个class
//反射不能破坏枚举
public enum EnumSing {
INSTANCE;
public EnumSing getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception{
EnumSing instance1 = EnumSing.INSTANCE;
// 枚举的构造器是有参数的
Constructor<EnumSing> declaredConstructor = EnumSing.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSing instance2 = declaredConstructor.newInstance();
System.out.println(instance1==instance2);
}
}
工厂模式:
简单工厂和抽象工厂,在增加新产品的时候都违反了开闭原则,只有工厂方法模式遵从了开闭原则,在实际应用时,根据实际情况,如果产品稳定,不经常
这张图概括了后面工厂模式的全部
工厂模式
简单工厂模式:不满足开闭原则
有car接口,他的实现类有WuLing,Tesla,工厂类CarFactory,调用工厂的类Customer
package factory.simple;
public interface Car {
void car();
}
package factory.simple;
public class WuLing implements Car{
@Override
public void car() {
System.out.println("五菱宏光");
}
}
package factory.simple;
public class Tesl implements Car{
@Override
public void car() {
System.out.println("特斯拉");
}
}
package factory.simple;
//简单工厂模式、静态工厂模式 //都使用静态方法
//不满足开闭原则,如果想满足开闭原则,会多付出很多代价
/**
* 如果此时多了一个新的车,如何加进去
* 1、修改getCar(),违反开闭原则
* 2、使用getWuLing(),getTesla()的方式创建Car对象,还是会修改CarFactory的类
*/
public class CarFactory {
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
}else if(car.equals("Tesla")){
return new WuLing();
}else{
return null;
}
}
}
package factory.simple;
public class Customer {
public static void main(String[] args) {
// 不使用工厂
Car car1 = new WuLing();
Car car2 = new Tesl();
System.out.println(car1);
System.out.println(car2);
// 使用工厂
Car car3 = CarFactory.getCar("五菱");
Car car4 = CarFactory.getCar("Tesla");
System.out.println(car3);
System.out.println(car4);
}
}
工厂方法模式:
还是上面的Car类,每个车有自己的工厂类,这些类都实现了各自的工厂接口,当有一个新的车时,只需创建他的实体类与工厂类,不需要修改代码,符合了开闭原则,但是每个车的实体类都需要一个工厂类,代价比较大。
package factory.method;
//工厂方法模式
public interface CarFactory {
Car getCar();
}
package factory.method;
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesl();
}
}
package factory.method;
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesl();
}
}
package factory.method;
import factory.simple.CarFactory;
import factory.simple.WuLing;
/**
* 工厂方法模式
* 每个车有自己的工厂类,这些类都实现了工厂接口
*/
public class Customer {
public static void main(String[] args) {
Car car1 = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
System.out.println(car1);
System.out.println(car2);
}
}
简单工厂模式与工厂方法模式对比
抽象工厂模式
产品组和产品等级结构
来源:https://blog.csdn.net/liuyuchen282828/article/details/100862118
案例的结构
案例的类图:
案例:HuaweiFactory华为的工厂,XiaomiFactory小米的工厂,HUAWEIPhone华为手机的工厂,HUAWEIRouter华为路由器的工厂,IPhoneProduct所有的手机工厂,IRouteFactory所有的路由器工厂,Client测试类。
package factory.abstract1;
//手机产品接口
public interface IphoneProdect {
void start();
void shutdown();
void callup();
void sendMS();
}
package factory.abstract1;
//路由器产品接口
public interface IRouteProduct {
void start();
void shutdown();
void openwifi();
void seeting();
}
工厂的工厂的接口IProductfactory:拥有手机产品、路由器产品的接口
package factory.abstract1;
public interface IProductfactory {
// 生产手机
IphoneProdect iphoneProdect();
// 生产路由器
IRouteProduct irouterProduct();
}
package factory.abstract1;
public class XiaomiFactory implements IProductfactory{
@Override
public IphoneProdect iphoneProdect() {
return new XiaomiPhone();
}
@Override
public IRouteProduct irouterProduct() {
return new XiaomiRouter();
}
}
package factory.abstract1;
public class XiaomiFactory implements IProductfactory{
@Override
public IphoneProdect iphoneProdect() {
return new XiaomiPhone();
}
@Override
public IRouteProduct irouterProduct() {
return new XiaomiRouter();
}
}
小米工厂,直接实现工厂的工厂接口就可以
package factory.abstract1;
public class XiaomiFactory implements IProductfactory{
@Override
public IphoneProdect iphoneProdect() {
return new XiaomiPhone();
}
@Override
public IRouteProduct irouterProduct() {
return new XiaomiRouter();
}
}
小米手机具体实现类:
package factory.abstract1;
//小米手机
public class XiaomiPhone implements IphoneProdect{
@Override
public void start() {
System.out.println("小米手机开机");
}
@Override
public void shutdown() {
System.out.println("小米手机关机");
}
@Override
public void callup() {
System.out.println("用小米手机打电话");
}
@Override
public void sendMS() {
System.out.println("用小米手机发短信");
}
}
小米路由器
package factory.abstract1;
public class XiaomiRouter implements IRouteProduct{
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void openwifi() {
System.out.println("小米打开wifi");
}
@Override
public void seeting() {
System.out.println("小米设置路由器");
}
}
华为的代码就不粘贴了
测试类:
package factory.abstract1;
public class Client {
public static void main(String[] args) {
System.out.println("小米系列");
// 小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
// 小米手机
xiaomiFactory.iphoneProdect().callup();
xiaomiFactory.iphoneProdect().sendMS();
// 小米路由器
xiaomiFactory.irouterProduct().seeting();
// 华为工厂
HuaweiFactory huaweiFactory = new HuaweiFactory();
// 华为手机
huaweiFactory.iphoneProdect().callup();
huaweiFactory.iphoneProdect().sendMS();
// 华为路由器
huaweiFactory.irouterProduct().seeting();
}
}
建造者模式
案例:
package factory.builder;
//抽象的建造者
public abstract class Builder {
abstract void buildA();//地基
abstract void buildB();//钢筋工程
abstract void buildC();//铺电线
abstract void buildD();//粉刷
abstract Product getProduct();
}
package factory.builder;
//具体的构建者:工人
public class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
@Override
void buildA() {
product.setBuildA("地基");
System.out.println("地基");
}
@Override
void buildB() {
product.setBuildB("钢筋工程");
System.out.println("钢筋工程");
}
@Override
void buildC() {
product.setBuildC("铺电线");
System.out.println("铺电线");
}
@Override
void buildD() {
product.setBuildD("粉刷墙面");
System.out.println("粉刷墙面");
}
@Override
Product getProduct() {
return product;
}
}
package factory.builder;
//产品:房子
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
package factory.builder;
//指挥:核心,负责指挥构建一个工程,工程如何构建,由他决定
public class Derector {
/**
* 指挥工人按照顺序建房子,先建A再B再C再D的顺序
* @param builder
* @return
*/
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
package factory.builder;
public class Test {
public static void main(String[] args) {
// 指挥
Derector derector = new Derector();
// 指挥工人完成产品
Product build = derector.build(new Worker());
System.out.println(build.toString());
}
}
另一个建造者模式的案例:
没有指挥者,任务顺序由Worker控制,可以自定义“套餐”。
package factory.builder.demo02;
import java.sql.PreparedStatement;
//抽象的建造者
public abstract class Builder {
abstract Builder buildA(String msg);//汉堡
abstract Builder buildB(String msg);//可乐
abstract Builder buildC(String msg);//薯条
abstract Builder buildD(String msg);//甜点
abstract Product getProduct();
}
package factory.builder.demo02;
//产品:套餐
public class Product {
private String buildA = "汉堡";
private String buildB = "可乐";
private String buildC = "薯条";
private String buildD = "甜点";
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
package factory.builder.demo02;
public class Worker extends Builder{
private Product product = new Product();
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
package factory.builder.demo02;
public class Test {
public static void main(String[] args) {
// 服务员
Worker worker = new Worker();
// 可以在原来的基础上自由组合
Product product = worker.buildA("鸡翅").buildB("雪碧").getProduct();
System.out.println(product.toString());
Product product2 = worker.getProduct();
System.out.println(product2.toString());
}
}
下一步原型模式