oop7大原则
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
- 依赖倒置原则:面向接口编程,不要面向实现编程
- 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性
- 接口隔离原则:要为各个类建立他们需要的专用接口
- 迪米特法则:只与你的直接朋友交谈,不跟”陌生人“说话
- 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑继承关系来实现
一、创建型模式
1、单例模式
- 核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 应用场景:Windows中的任务管理器;数据库连接池;项目中的配置文件类等等。
- 优点:减少了系统性能开销。
- 常见场景:
- Windows的任务管理器
- Windows的回收站
- 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
- 网站的计数器一般也会采用单例模式,可以保证同步
- 数据库连接池的设计一般也是单例模式
- 在Servlet编程中,每个Servlet也是单例的
- 在Spring中,每一个Bean默认就是单例的
- ......
重要的原则:构造器私有
1、饿汉式 (线程安全,调用效率高。但是,不能延时加载)
package com.znd.single;
//饿汉式单例
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 getInstance(){
return HUNGRY;
}
}
类初始化时立即加载,类初始化时,处于天然的线程安全模式,因此线程安全,方法没有同步,效率高。而且可能存在浪费内存的问题
2、DCL懒汉式(单例对象延迟加载)
真正用的时候才加载,资源利用率高了,但每次调用getInstance()方法都要同步,并发效率低。
package com.znd.single;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
//懒汉式单例
//道高一尺魔高一丈
public class LazyMan {
//红绿灯
private static boolean znd = false;
private LazyMan(){
if (znd==false){
znd=true;
}else {
throw new RuntimeException("不要试图用反射破坏异常");
}
// synchronized (LazyMan.class){
// if (lazyMan!=null){
// throw new RuntimeException("不要试图用反射破坏异常");
// }
// }
}
//volatile必免指令重排
private volatile static LazyMan lazyMan;
//双重检测锁模式的懒汉式单例 DCL 懒汉式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan(); //不是一个原子性操作
}
}
}
return lazyMan;
}
//多线程并发
// public static void main(String[] args) {
// for (int i = 0; i < 10; i++) {
// new Thread(()->{
// lazyMan.getInstance();
// }).start();
// }
// }
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*
* 指令重排
* 123
* 132 A
* B //次时lazyman还没有完成构造
*/
// 反射!
public static void main(String[] args) throws Exception {
// LazyMan instance1 = LazyMan.getInstance();
Field znd = LazyMan.class.getDeclaredField("znd");
znd.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
znd.set(instance2,false);
LazyMan instance3 = declaredConstructor.newInstance();
// System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
}
}
3、静态内部类
外部类没有static属性,则不会像饿汉式那样立即加载对象,只有真正调用getInstance(),才会加载静态内部类。加载 类时是线程安全的,兼并了并发高效调用和延迟加载的优势。
package com.znd.single;
//静态内部类
public class Holder {
private Holder(){
}
private static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,因为有反射
4、枚举
实现较简单,枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞。
缺点是无延迟加载。
package com.znd.single;
import java.lang.reflect.Constructor;
//enum 是什么? 本身也是一个类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
//Exception in thread "main" java.lang.NoSuchMethodException: com.znd.single.EnumSingle.<init>()
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举的最终反编译源码
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.znd.single;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/znd/single/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
2、工厂模式
OOP七大原则
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要正对实现编程
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
简单工厂模式(静态工厂模式)
package com.znd.factory.simple;
public class CarFactory {
public static Car getCar(String car) {
//方法一
if ("五菱".equals(car)){
return new WuLing();
}else if ("特斯拉".equals(car)){
return new Tesla();
}else {
return null;
}
}
//方法二
public static Car getWuLing(){
return new WuLing();
}
public static Car getTesla(){
return new Tesla();
}
}
package com.znd.factory.simple;
public class Consumer {
public static void main(String[] args) {
//传统new
// Car car1 = new WuLing();
// Car car2 = new Tesla();
//方法一 if逻辑
// Car car1 = CarFactory.getCar("五菱");
// Car car2 = CarFactory.getCar("特斯拉");
//方法二 单独静态方法
Car car1 = CarFactory.getWuLing();
Car car2 = CarFactory.getTesla();
car1.name();
car2.name();
}
}
!!不满足开闭原则,添加类还是需要修改CarFactory的代码
工厂方法模式
package com.znd.factory.method;
public interface CarFactory {
Car getCar();
}
package com.znd.factory.method;
public class Consumer {
public static void main(String[] args) {
Car car1 = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
car1.name();
car2.name();
Car car3 = new MoBaiFactory().getCar();
car3.name();
}
}
新增一种车就新增一个实现类工厂。满足开闭原则。
!! 但是代码复杂度变高
- 结构复杂度 simple胜
- 代码复杂度 simple胜
- 编程复杂度 simple胜
- 管理复杂度 simple胜
- 根据设计原则 method胜出
- 根据实际业务 选择简单工厂模式!
3、抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类
package com.znd.factory.abstract1;
public class Client {
public static void main(String[] args) {
System.out.println("===========小米系列产品============");
XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();
IPhoneProduct iPhoneProduct = xiaoMiFactory.iphoneProduct();
iPhoneProduct.callUp();
iPhoneProduct.sendSMS();
iPhoneProduct.shutdown();
iPhoneProduct.start();
IRouterProduct iRouterProduct = xiaoMiFactory.irouterProduct();
iRouterProduct.openWifi();
iRouterProduct.setting();
iRouterProduct.start();
iRouterProduct.shutdown();
System.out.println("===========华为系列产品============");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IPhoneProduct iPhoneProduct1 = huaweiFactory.iphoneProduct();
iPhoneProduct1.start();
iPhoneProduct1.shutdown();
iPhoneProduct1.sendSMS();
iPhoneProduct1.callUp();
IRouterProduct iRouterProduct1 = huaweiFactory.irouterProduct();
iRouterProduct1.shutdown();
iRouterProduct1.start();
iRouterProduct1.setting();
iRouterProduct1.openWifi();
}
}
虽然不满足开闭原则,但是若长期稳定,修改还是很有必要的。
4、创造者模式
-
它提供了一种创建对象的最佳方式
-
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不用的表示
-
主要作用:在用户不知道 对象的建造过程和细节 的情况下就可以直接创建复杂的对象
-
用户只需要给出指定复杂的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
-
例子:
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车时怎么组装的(车轮、车门、发动机、方向盘等等))
建房子过程:
- 地基
- 钢筋工程
- 铺电线
- 粉刷
如果要盖房子,首先要找一个承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品),最后验收。
常规用法
- 抽象的Builder
package com.znd.factory.builder;
//抽象的建造者:方法
public abstract class Builder {
abstract void buildA(); //地基
abstract void buildB(); //钢筋工程
abstract void buildC(); //铺电线
abstract void buildD(); //粉刷
abstract Product getProduct();
}
- 具体的Builder(工人)
package com.znd.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;
}
}
- 产品product
package com.znd.factory.builder;
//产品:房子
public class Product {
private String buildA;
private String buildB;
private String buildC;
private String buildD;
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + 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;
}
}
- 指挥Director(控制产品的先后顺序)
package com.znd.factory.builder;
//指挥:核心,负责指挥构建一个工程,工程如何构建,由他决定
public class Director {
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildD();
builder.buildC();
return builder.getProduct();
}
}
- test
package com.znd.factory.builder;
public class Test {
public static void main(String[] args) {
//指挥
Director director = new Director();
//指挥 具体的工人完成 产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
静态内部类
客户来充当指挥
服务员(具体的建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。比第一种方式少了指挥者,主要时给了客户指挥的权力。时产品的创建更加灵活。
- 产品类Product
package com.znd.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 + '\'' +
'}';
}
}
- 抽象类建造者Builder
package com.znd.factory.builder.demo02;
//抽象建造者
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();
}
- 具体实现类Worker
package com.znd.factory.builder.demo02;
//具体的建造者
public class Worker extends Builder {
private Product product;
public Worker() {
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;
}
}
- test
package com.znd.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());
}
}
产品和建造使用分离,实现了解耦,符合开闭原则
5、原型模式
浅克隆
package com.znd.factory.prototype.demo01;
import java.util.Date;
/**
* 1、实现一个接口 Cloneable
* 2、重写一个方法 clone
*/
public class Video implements Cloneable {
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
package com.znd.factory.prototype.demo01;
import java.util.Date;
//客户端克隆
public class BiliBili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象v1
Date date = new Date();
Video v1 = new Video("徐大SAO", date);
//v1克隆v2
//Video v2 = new Video("徐大SAO", date);
Video v2 = (Video) v1.clone();
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
System.out.println("=================");
date.setTime(234341231);
v2.setName("双人鱼");
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
// System.out.println("v1=>hash:"+v1.hashCode());
// System.out.println("v2=>hash:"+v2.hashCode());
}
}
深克隆
package com.znd.factory.prototype.demo01;
import java.util.Date;
//客户端克隆
public class BiliBili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象v1
Date date = new Date();
Video v1 = new Video("徐大SAO", date);
//v1克隆v2
//Video v2 = new Video("徐大SAO", date);
Video v2 = (Video) v1.clone();
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
System.out.println("=================");
date.setTime(234341231);
v2.setName("双人鱼");
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
// System.out.println("v1=>hash:"+v1.hashCode());
// System.out.println("v2=>hash:"+v2.hashCode());
}
}
package com.znd.factory.prototype.demo02;
import java.util.Date;
//Spring Bean:单例模式,原型模式
//原型模式 + 工厂模式 ==> new <==>原型模式
public class BiliBili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象v1
Date date = new Date();
Video v1 = new Video("徐大SAO", date);
//v1克隆v2
//Video v2 = new Video("徐大SAO", date);
Video v2 = (Video) v1.clone();
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
System.out.println("=================");
date.setTime(234341231);
v2.setName("双人鱼");
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
}
}
通过克隆可以提高效率,如果对象很复杂,使用原型模式很有必要。