设计模式不是语法规定,就是提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。是前辈对代码开发经验的总结,解决问题的一系列套路
面向对象设计原则的实际运用,是对类的封装、继承、多态以及类的关联关系和组合关系的充分理解。
一个设计模式:
模式名称
问题
解决方案
效果
OOP:Object Oriented Programming 面向对象的程序设计
对扩展开放、对修改关闭(核心就是不改变源代码)
一个实体允许在不改变它的源代码情况下变更它的行为(就是说可以扩展它的行为,但是最好不要修改)
当一个对象已经有两个行为了,可以通过建设新的类,让这个类继承新类进而实现行为的扩展(这里指的继承也只是一种实现扩展的方式),而不是通过修改原来的类代码进行增加新的行为。
继承必须确保父类所拥有的性质在子类中仍然成立(子类可以扩展父类的功能,但是不要改版父类原有的功能)
子类继承父类时,可以添加新的方法完成新增功能外,尽量不要重写父类的方法
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。
面向接口编程(降低耦合性),不要面向实现编程。
抽象不依赖具体、具体依赖抽象
控制类的粒度大小、将对象解耦、提高其内聚性
一个类不应该承担太多的职责,一个类只用做好一件事就可以
要为各个类建立它们需要的专用接口
只和直接朋友交谈,不和“陌生人”说话。放在框架里就是不跨层进行交流
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
主要是创建对象
提供了一种访问对象的唯一方式,保证一个类只有这一个实例。单例的关键就是:私有化构造函数
不能通过构造函数生成对象
例子:
这是饿汉式单例,直接一上来就创建对象
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
测试单例:这个类只有一个实例
public class SingletonTest {
public static void main(String[] args) {
HungrySingle hungrySingle1=HungrySingle.getInstance();
HungrySingle hungrySingle2=HungrySingle.getInstance();
hungrySingle1.setName("11");
hungrySingle2.setName("22");
hungrySingle1.showName(); //22
hungrySingle2.showName(); //22
if(hungrySingle1==hungrySingle2){
System.out.println("same"); // same
}else{
System.out.println("different");
}
}
}
①饿汉式:
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。 优点:没有加锁,执行效率会提高。 缺点:类加载时就初始化,浪费内存。
/**
* 饿汉式单例
* 上来就创建对象
* 如果对象中有占用比较大内存的数组之类的
* 就会比较耗内存
* 所以出现了:懒汉式单例用的时候创建对象
*/
public class HungrySingle {
private HungrySingle() {
}
private final static HungrySingle HUNGRY_SINGLE=new HungrySingle();
public static HungrySingle getInstance(){
return HUNGRY_SINGLE;
}
}
②懒汉式:
一般用的是懒汉式单例中线程安全的部分即加锁
线程不安全:没加锁
是否 Lazy 初始化:是
是否多线程安全:否
实现难度:易
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全:加锁
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:易
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
③DCL单例(double-checked locking)双重检验计加锁模式
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:较复杂
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;
}
}
④静态内部类
是Singleton类中还存在一个静态类SingletonHolder,在静态类SingletonHolder中采用的是饿汉式单例,但是与饿汉式单例不同的是:Singleton类被装载了但是instance不一定被初始化(而饿汉式单例是singleton类被装载了instance一定被初始化了)
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:一般
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
⑤枚举
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
数据库连接池、线程池、windows任务管理器、windows的回收站、操作系统的文件管理器
实现创建者和使用者的分离(使用者不需要去直接创建对象(new),而是让工厂去创建对象,使用者直接去工产拿对象)
满足开闭原则、依赖倒装原则、迪米特法则
核心原则:实例化对象不使用new,用工厂方法代替(比如安卓中的viewmodel就是用factory实例化的 ) 将调用者和实现类进行解耦的操作
①简单(静态)工产模式
只有一个工产
对于car而言,有两个实现类carImpl和carImpl1;
如果consumer需要这两个车,按照原有的方式就是new这两个实现类
//用原始的new方法 // Car car=new CarImpl(); // Car car1=new CarImpl1();
如果按照简单工产模式:
就是新建一个CarFactory,在这个factory中创建两个car对象,在consumer中利用factory生成对象
/**8
* 简单工厂模式(静态工产模式)
* 不满足开闭原则
* 增加一个新的产品,必须修改代码
*/
public class CarFactory {
//如果需要增加一个车的话,那就需要修改代码逻辑
// (不满足开闭原则)
//可以进行一次优化:可以直接getCar1(),但是这个方法
//也会出现需要修改这个类
public static Car getCar(String car){
if(car.equals("car")){
return new CarImpl();
}else if(car.equals("car1")){
return new CarImpl1();
}else{
return null;
}
}
}
//用工厂模式创建
Car car=CarFactory.getCar("car");
Car car1=CarFactory.getCar("car1");
car.name();
car1.name();
但是简单工产模式存在一个比较大的问题就是:
不满足开闭原则,当需要增加一个car时需要去修改原有的代码
但是从代码复杂度、管理角度而言,simpleFactory是有很大优势的,所以在实际运用的角度而言一般选择simple
②工产方法模式
有多个工产,满足开闭原则
因为简单工产模式不满足开闭原则,进而提出工产方法模式解决这个问题
工产方法实际上就是每个车都有一个车工产,car1有一个Car1Factory,car2有一个Car2
Fartory.....如果需要增加car的话,就不需要修改原有的代码,直接增加一个Car3Factory就可
/**
* 工厂方法模式
*/
public interface CarFactory {
Car getCar();
}
/**
* 每个车都有自己的工产
* 如果该需要增加车那直接增加那个车的工产就行
*/
public class Car1Factory implements CarFactory {
@Override
public Car getCar() {
return new CarImpl1();
}
}
public class Consumer {
public static void main(String[] args) {
Car car1=new Car1Factory().getCar();
}
}
但是这个实现方式,导致代码量增多,代码管理难度也加大,在实际运用中不是很常用
一个超级工产创建其他的工厂
产品族:
抽象工产扩充产品族难,扩充产品等级简单
产品:
phone:
/**8
* 手机产品接口
*/
public interface IphoneProduct {
void start();
void shut();
void callup();
void send();
}
package com.study.shejiPattern.factory.abstract1;
//小米手机
public class XiaomiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shut() {
System.out.println("小米手机");
}
@Override
public void callup() {
System.out.println("小米手机");
}
@Override
public void send() {
System.out.println("小米手机");
}
}
package com.study.shejiPattern.factory.abstract1;
public class HuaWeiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("huawei");
}
@Override
public void shut() {
System.out.println("huawei");
}
@Override
public void callup() {
System.out.println("huawei");
}
@Override
public void send() {
System.out.println("huawei");
}
}
router:
/**
* 路由器产品
*/
public interface IRouterProduct {
void start();
void shut();
void openWifi();
void setting();
}
huawei、xiaomi与上面的一致
工产:
xiaomi和huawei的工产都需要实现这个抽象类工产
package com.study.shejiPattern.factory.abstract1;
//抽象产品工产
public interface IProductFactory {
//生产手机
IphoneProduct iphoneProduct();
//生产路由器
IRouterProduct routeProduct();
}
package com.study.shejiPattern.factory.abstract1;
public class HuaweiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new HuaWeiPhone();
}
@Override
public IRouterProduct routeProduct() {
return new HuweiRouter();
}
}
package com.study.shejiPattern.factory.abstract1;
/**
* 小米生产
*/
public class XiaomiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct routeProduct() {
return new XiaomiRouter();
}
}
client:
package com.study.shejiPattern.factory.abstract1; public class Client { public static void main(String[] args) { System.out.println("----------xiaomi---"); XiaomiFactory xiaomiFactory=new XiaomiFactory(); IphoneProduct iphoneProduct=xiaomiFactory.iphoneProduct(); iphoneProduct.start(); IRouterProduct xiaomaR=xiaomiFactory.routeProduct(); xiaomaR.openWifi(); System.out.println("----------huawei---"); HuaweiFactory huaweiFactory=new HuaweiFactory(); IphoneProduct huawei=huaweiFactory.iphoneProduct(); huawei.start(); IRouterProduct huaweiR=huaweiFactory.routeProduct(); huaweiR.openWifi(); } }
当需要增加一个产品时,会需要改很多代码
扩充产品族:难
扩充产品等级:简单
建造复杂对象 将复杂对象的构建与他的表示分离
应用场景:需要生产的产品对象有复杂的内部结构,但是这些产品具有共性,指挥可以创建不用的产品
/**
* 抽象的建造者
* 产品建造的方法 ----具体产品的建造方法去Worker中实现
*
*/
public abstract class Builder {
abstract void buildA();
abstract void buildB();
abstract Product getProduct();
}
package com.study.shejiPattern.builder.demo;
/**
* 具体的建造者:工人继承Builder类
*/
public class Worker extends Builder {
private Product product;
public Worker() {
product=new Product();
}
@Override
void buildA() {
product.setBuildA("A");
System.out.println("A");
}
@Override
void buildB() {
product.setBuildB("B");
System.out.println("B");
}
@Override
Product getProduct() {
return product;
}
}
package com.study.shejiPattern.builder.demo;
/**
* 产品
*/
public class Product {
private String buildA;
private String buildB;
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;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
'}';
}
}
package com.study.shejiPattern.builder.demo;
/**
* 指挥构建工程
* 指挥建构者如何构建产品 调用先后次序
*/
public class Director {
//指挥工人构建房子 对象的构建过程
public Product build(Builder builder){
builder.buildA();
builder.buildB();
return builder.getProduct();
}
}
package com.study.shejiPattern.builder.demo;
public class Client {
public static void main(String[] args) {
//指挥
Director director=new Director();
//指挥具体的工人
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
概念:创建复杂对象时,可以创建当前对象的克隆。创建重复对象。
主要就是可以被克隆的对象实现一个接口Cloneable重写clone()
主要是分为浅克隆和深克隆
直接重写父类的clone()
package com.study.shejiPattern.prototype;
import java.util.Date;
/**
* 需要被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;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", 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;
}
}
package com.study.shejiPattern.prototype;
import java.text.ParseException;
import java.util.Date;
public class Bilili {
public static void main(String[] args) throws CloneNotSupportedException, ParseException {
Date date=new Date();
Video v1 = new Video("java", date);
Video v2 = (Video) v1.clone();
System.out.println("v1:"+v1);
System.out.println("v2:"+v2);
// System.out.println("v1HashCode:"+v1.hashCode());
// System.out.println("v2HashCode:"+v2.hashCode());
// System.out.println("=============");
// SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyyMMdd");
// v1.setCreateTime(simpleDateFormat.parse("20221023"));
date.setTime(22222);
System.out.println("v1:"+v1);
System.out.println("v2:"+v2);
// System.out.println("=============");
// v2.setCreateTime(simpleDateFormat.parse("20221024"));
// System.out.println("v1:"+v1);
// System.out.println("v2:"+v2);
}
}
改变date值之后:v1和v2一起改变
克隆前后的对象指向同一个Date对象,所以当Date对象发生改变时,V1和v2一起改变
package com.study.shejiPattern.prototype;
import java.util.Date;
/***
* 实现深克隆
* 重写clone方法(最好的方法)
* 不然就的利用序列化和反序列进行深克隆
*/
public class Video1 implements Cloneable {
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj=super.clone();
Video1 v= (Video1) obj;
v.createTime= (Date) this.createTime.clone();
return obj;
}
public Video1(){
}
public Video1(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", 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;
}
}
package com.study.shejiPattern.prototype;
import java.util.Date;
public class Bilili1 {
public static void main(String[] args) throws CloneNotSupportedException {
Date date=new Date();
Video1 v1 = new Video1("java", date);
Video1 v2 = (Video1) v1.clone();
System.out.println("v1:"+v1);
System.out.println("v2:"+v2);
// System.out.println("v1HashCode:"+v1.hashCode());
// System.out.println("v2HashCode:"+v2.hashCode());
date.setTime(22222);
System.out.println("v1:"+v1);
System.out.println("v2:"+v2);
}
}
改变date值之后只有V1改变了,V2并没有改变
V1和v2指向两个date,当date改变时实际上是V1指向的date发生改变,所以V1的时间发生改变,但是v2并没有改变。