配合 尚硅谷Java设计模式 学习效果更佳!视频连接
设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,
模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)
代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时
间的试验和错误总结出来的。
设计模式的本质提高 软件的扩展性,维护性,通用性和灵活性,并降低软件的复杂
度。
<<设计模式>> 是经典的书,作者是 Erich Gamma、Richard Helm、Ralph
Johnson 和 John Vlissides Design(俗称 “四人组 GOF”)
设计模式并不局限于某种语言,java,php,c++ 都有设计模式
软件工程中,设计模式 (design pattern) 是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学
大厦 VS 简易房
一栋大厦的建造需要经过各种可行性研究,结构规划,以及许多设计人员参与设计与调研才能在正确的实施。
而简易房无需那么多的步骤也可以建造成功,但是相应的在高度上,稳定性上会远远落后于大厦。
拿实际工作经历来说, 当一个项目开发完后,如果客户提出增新功能,怎么办?
如果项目开发完后,原来程序员离职,你接手维护该项目怎么办? (维护性[可读性、规范性])
目前程序员门槛越来越高,一线IT公司(大厂),都会问你在实际项目中使用过什么设计模式,怎样使用的,解决了什么问题。
设计模式在软件中哪里?面向对象(oo)=>功能模块[设计模式+算法(数据结构)] =>框架[使用到多种设计模式] => 架构 [服务器集群]
如果想成为合格软件工程师,那就花时间来研究下设计模式是非常必要的.
编写软件过程中,程序员面临着来自耦合性,内聚性 以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好的
分享金句: 设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”
Scott Mayers 在其巨著《Effective C++》就曾经说过:C++老手和 C++新手的区别就是前者手背上有很多伤疤。
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
饿汉式(静态常量)
单例模式之饿汉式(静态常量)
私有化构造器,防止外部new
类的内部创建对象
向外暴露一个公共方法获取该实例
public class Singleton01 {
private Singleton01(){}
private final static Singleton01 INSTANCE = new Singleton01();
public static Singleton01 getInstance(){
return INSTANCE;
}
}
饿汉式(静态代码块)
单例模式之饿汉式(静态代码块)
私有化构造器,防止外部new
类的内部创建对象
向外暴露一个公共方法获取该实例
public class Singleton02 {
private Singleton02(){}
private final static Singleton02 INSTANCE;
static{
INSTANCE = new Singleton02();
}
public static Singleton02 getInstance(){
return INSTANCE;
}
}
优缺点:
懒汉式(非线程安全)
单例模式之懒汉式(非线程安全)
提供公有方法,当调用getInstance方法时,才会创建实例
public class Singleton03 {
private Singleton03(){}
private static Singleton03 instance;
public static Singleton03 getInstance(){
if(instance == null){
instance = new Singleton03();
}
return instance;
}
}
优缺点:
懒汉式(线程安全)
单例模式之懒汉式(线程安全)
提供公有方法,当调用getInstance方法时,才会创建实例
public class Singleton04 {
private Singleton04(){}
private static Singleton04 instance;
// 使用同步代码块,解决线程安全问题
public static synchronized Singleton04 getInstance(){
if(instance == null){
instance = new Singleton04();
}
return instance;
}
}
优缺点:
双重检查(推荐使用)
单例模式之双重检查
提供公有方法,当调用getInstance方法时,才会创建实例
public class Singleton05 {
private Singleton05(){}
// volatile 关键字表示:
// 对变量的操作会立即更新到主存中,即该变量的改变是其它线程立即可见的
private static volatile Singleton05 instance;
// 使用双重检查机制,解决线程安全问题,同时解决懒加载懒加载问题
public static Singleton05 getInstance(){
if(instance == null){
synchronized(Singleton05.class){
if(instance == null){
instance = new Singleton05();
}
}
}
return instance;
}
}
优缺点:
静态内部类(推荐使用)
单例模式之静态内部类
*/
public class Singleton06 {
private Singleton06(){}
/*
静态内部类
在外部类加载的时候静态内部类不会跟随一起加载
只有在用到的时候才会加载
并且在类加载的是线程安全的
*/
private static class SingletonInstance{
private final static Singleton06 INSTANCE = new Singleton06();
}
// 使用这种方式实现单例模式既解决了线程安全问题,同时解决了懒加载问题
public Singleton06 getInstance(){
return SingletonInstance.INSTANCE;
}
}
优缺点:
枚举(推荐使用)
public class Test{
public static void main(String[] args){
Singleton07 instance = Singleton07.INSTANCE;
instance.sayHello();
}
}
使用枚举的方式实现单例模式
enum Singleton07 {
INSTANCE;
public void sayHello(){
System.out.println("hello");
}
}
优缺点:
简单工厂模式
基本介绍:
/*
简单工厂
封装实例化对象的代码,专门用来实例化对象
如果生成对象的逻辑有变化,则直接修改工厂类即可
*/
public class SimpleFactory {
// 外部通过SimpleFactory.creatPizza()获取pizza实例
public static Pizza creatPizza(String name){
Pizza pizza = new Pizza(name);
return pizza;
}
}
mian(){
Pizza pizza = SimpleFactory.creatPizza();
}
工厂方法模式
基本介绍:
工厂方法模式
将具体地创建逻辑下沉到子类,由子类来决定对象应该如何创建
public abstract class OrderPizza {
// 抽象方法,由子类来决定具体地实现逻辑
abstract Pizza creatPizza();
}
public class OrderHamPizza extends OrderPizza{
@Override
Pizza creatPizza() {
return new Pizza("火腿");
}
}
public class OrderCheesePizza extends OrderPizza{
@Override
Pizza creatPizza() {
return new Pizza("芝士");
}
}
main(){
// 创建火腿pizza
OrderPizza op = new OrderHamPizza();
Pizza hamPizza = op.creatPizza();
// 创建芝士pizza
op = new OrderCheesePizza();
Pizza cheesePizza = op.creatPizza();
}
抽象工厂模式
基本介绍:
抽象工厂模式
定义创建对象的接口,由子类工厂提供创建对象的具体逻辑
需要注意的是,当需使用工厂类时,使用者只需要聚合这个接口即可
public interface AbsFactory {
Pizza creatPizza();
}
火腿pizza工厂类
public class HamPizzaFactory implements AbsFactory{
@Override
public Pizza creatPizza() {
return new Pizza("火腿");
}
}
芝士pizza工厂类
public class CheesePizzaFactory implements AbsFactory{
@Override
public Pizza creatPizza() {
return new Pizza("芝士");
}
}
只需要聚合AbsFactory,即可使用所有的工厂子类
即把单个简单工厂变成了工厂簇
更利于代码的维护和扩展
public class OrderPizza {
这个工厂可能是火腿工厂,也可能是芝士工厂
这就将单个简单工厂变成了很多个可选的工厂子类
如果需要有新的工厂类,只需要实现AbsFactory即可使用
AbsFactory factory;
void setFactory(AbsFactory factory){
this.factory = factory;
}
Pizza order(){
return factory.creatPizza();
}
public static void main(String[] args) {
OrderPizza op = new OrderPizza();
op.setFactory(new HamPizzaFactory());
Pizza hamPizza = op.order();
op.setFactory(new CheesePizzaFactory());
Pizza cheesePizza = op.order();
}
}
总结
工厂模式的意义
基本介绍:
原型模式
即想要克隆的对象类型需要实现 Cloneable 这个接口
实现这个接口代表这个类能够克隆并且拥有克隆的能力
当需要克隆的时候直接调用clone这个方法即可
并且当对象的结构发生变化的时候,clone() 依旧是有效的
@Data
@ToString
public class Sheep implements Cloneable{
private String name;
private Integer age;
@Override
public Sheep clone() {
try {
Sheep clone = (Sheep) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
main(){
Sheep sheep = new Sheep();
sheep.setAge(10);
sheep.setName("小羊");
Sheep[] sheeps = new Sheep[5];
// 克隆五只一样的羊
for(int i = 0; i < 5; ++i){
sheeps[i] = sheep.clone();
}
for(Sheep s : sheeps) System.out.println(s);
}
原型模式之深拷贝
基本介绍:
深拷贝之重写clone方法
@Data
@ToString
public class Sheep implements Cloneable, Serializable{
private String name;
private Integer age;
private CloneTest test;
@Override
public Sheep clone() {
try {
Sheep sheep = (Sheep) super.clone();
// 单独对引用数据类型进行拷贝
sheep.test = (CloneTest) test.clone();
return sheep;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
class CloneTest implements Cloneable, Serializable{
String text;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
深拷贝之反序列化
@Override
public Sheep clone() {
ByteArrayInputStream bis = null;
ByteArrayOutputStream bos = null;
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Sheep) ois.readObject();
} catch (Exception e) {
throw new AssertionError();
}finally {
try {
bis.close();
bos.close();
oos.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结
基本介绍:
建造者模式的四个角色:
基本介绍
工作原理
目标:假设插孔电压220V, 手机充电需要5V, 利用类适配模式器将220V电压降至5V
输出220v的类
public class Output220V {
public int output220V(){
return 220;
}
}
输出5V的接口
public interface Output5V {
int output5V();
}
构建Adapter类让他继承Output220V 实现Output5V
在其中完成适配,即让电压降至5V
public class MyAdapter extends Output220V implements Output5V{
@Override
public int output5V() {
return output220V() / 44;
}
}
main(){
Output5V op = new MyAdapter();
op.output5V(); // 输出 5v
}
注意事项
对象适配器 (常用)
基本介绍
目标:假设插孔电压220V, 手机充电需要5V, 利用类适配模式器将220V电压降至5V
其它的相较于类适配器来说都不用变,对于适配类来说用聚合关系替换继承关系即可
构建Adapter类让他实现Output5V 并聚合一个Output220V
在其中完成适配,即让电压降至5V
public class MyAdapter implements Output5V{
private Output220V op220;
public MyAdapter(Output220V op220){
this.op220 = op220;
}
@Override
public int output5V() {
return op220.output220V() / 44;
}
}
main(){
Output5V op = new MyAdapter(new Output220V());
op.output5V(); // 输出 5v
}
注意事项
接口适配器
注意事项
基本介绍
基本介绍
基本介绍
策略接口
public interface Speakable {
void speak();
}
策略接口
public interface FlyAble {
void fly();
}
本来可以实现speak和fly接口,然后让子类自己定义这两个行为
但是使用策略模式会更加灵活,子类也无需实现具体的方法
当子类需要更换fly逻辑时,不需要修改源码,只需要传入一个fly接口的实例,即可重写定义fly方法的逻辑
即外面传什么进来,鸭子就怎么飞
对于具体的操作,得看聚合进来的策略实例
多使用关联代替继承、实现 —— 合成复用原则
public abstract class Duck {
FlyAble flyAble; // 飞翔策略接口
Speakable sa; // 说话策略接口
void fly(){
flyAble.fly(); // 调用策略实例的fly方法,即是否能飞翔完全取决于外部传入的策略
}
void speak(){
sa.speak(); // 调用策略实例的speak方法,即是否能说话完全取决于外部传入的策略
}
public void setFlyAble(FlyAble flyAble){
this.flyAble = flyAble;
}
public void setSa(Speakable sa) {
this.sa = sa;
}
}
基本介绍
基本介绍
用责任链模式来完成器材订购案例,金额<=5000由主任批准,金额<=20000由校长批准,其它金额由董事批准
处理请求的抽象接收者,类图中的Handler
public abstract class Approve {
Approve approve; // 后继接收者
public void setApprove(Approve approve) { // 设值后继接收者
this.approve = approve;
}
public abstract void process(Request request); // 处理请求的方法
}
具体的请求
public class Request {
private int id; // 请求id
private float price; // 价格
public Request(int id, float price) {
this.id = id;
this.price = price;
}
public int getId() {return id;}
public float getPrice() {return price;}
}
具体接收者之主任,后继为校长
public class SeniorApprove extends Approve{
@Override
public void process(Request request) {
if(request.getPrice() <= 5000) {
System.out.println("订单 " + request.getId() + " 金额<=5000,由主任批准");
}else {
approve.process(request); // 主任无法处理,则传给后继接收者处理
}
}
}
具体接收者之校长,后继为董事
public class HeadmasterApprove extends Approve{
@Override
public void process(Request request) {
if(request.getPrice() <= 20000){
System.out.println("订单 "+request.getId()+" 金额<=20000,由校长批准");
}else {
approve.process(request); // 校长无法处理,传给后继接收者
}
}
}
具体接收者之董事,无后继,即所有价格在这里都可以被处理
public class DirectorApprove extends Approve{
@Override
public void process(Request request) {
System.out.println("订单 "+request.getId()+" 金额过大,由董事批准");
}
}
main(){
Approve senior = new SeniorApprove(); // 接收者之主任
Approve head = new HeadmasterApprove(); // 接收者之校长
Approve director = new DirectorApprove(); // 接收者之董事
// 构建责任链 主任的后继是校长,校长的后继是董事
senior.setApprove(head);
head.setApprove(director);
// 三种请求调用
Request request1 = new Request(1, 4000);
Request request2 = new Request(2,15000);
Request request3 = new Request(3,50000);
senior.process(request3); // 订单 3 金额过大,由董事批准
senior.process(request2); // 订单 2 金额<=20000,由校长批准
senior.process(request1); // 订单 1 金额<=5000,由主任批准
}
基本介绍
类图
模板方法模式完成豆浆制作,除了选择的材料不同外,其它的步骤完全一致
public abstract class SoyMilk {
// 模板方法,即定义了算法的执行流程
void make(){
select();
soak();
add();
stir();
}
// 选择原材,制作什么类型的豆浆,就选择什么材料,由子类实现
abstract void select();
// 添加到豆浆机中
void add(){ System.out.println("添加到豆浆机中"); };
// 侵泡
void soak(){ System.out.println("侵泡原材料"); }
// 搅拌
void stir(){ System.out.println("搅拌"); }
}
黄豆制作豆浆
public class Soy extends SoyMilk{
@Override
void select() {
System.out.println("选择原材料 黄豆");
}
}
红豆制作豆浆
public class Red extends SoyMilk{
@Override
void select() {
System.out.println("选择原材料 红豆");
}
}
main(){
SoyMilk soyMilk = new Red();
soyMilk.make(); // 模板方法 制作红豆豆浆
soyMilk = new Soy();
soyMilk.make(); // 模板方法 制作黄豆豆浆
}
模板方法模式在Spring IOC容器初始化的时候会用到,refresh()方法就是模板方法,内部定义了一套容器初始化的执行流程。
基本介绍
静态代理
静态代理在使用时,需要定义接口或者父类。被代理对象(即目标对象)与代理对象一起实现相同接口或者是继承相同父类
类图
代理对象与被代理对象一起实现的接口
public interface ITeacherDao {
void teach();
}
代理类,聚合一个被代理对象
public class TeacherDaoProxy implements ITeacherDao{
ITeacherDao dao; // 被代理对象
public TeacherDaoProxy(ITeacherDao dao){
this.dao = dao;
}
@Override
public void teach() {
System.out.println("进入代理方法");
dao.teach(); // 调用代理对象的方法, 可以再调用之前或者之后进行功能扩展
System.out.println("退出代理方法");
}
}
被代理类
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("调用了 teach 方法,开始教书");
}
}
通过调用代理对象的teach,完成对目标对象的调用
public static void main(String[] args) {
TeacherDao dao = new TeacherDao();
ITeacherDao proxy = new TeacherDaoProxy(dao); // 代理teacherDao
// 通过代理对象,调用目标方法
proxy.teach();
/* 控制台输出
进入代理方法
调用了 teach 方法,开始教书
退出代理方法
*/
}
动态代理
类图
获取动态代理对象的工厂类
public class ProxyFactory {
ITeacherDao teacherDao;
public ProxyFactory(ITeacherDao teacherDao){
this.teacherDao = teacherDao;
}
利用java的反射机制,动态的在内存中构建代理对象
public Object getProxyInstance(){
/*
Proxy.newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
方法的三个参数:
loader 目标对象的ClassLoader, 利用固定的方法获取
interfaces 目标对象实现的接口, 利用固定的方法获取
InvocationHandler 事件处理 执行目标对象的方法时,会触发事件处理器,把目标对象的方法作为一个参数传入
*/
return Proxy.newProxyInstance(teacherDao.getClass().getClassLoader(),
teacherDao.getClass().getInterfaces(),
new InvocationHandler() {
@Override // method 目标方法 args 调用目标方法时,传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入动态代理方法");
Object returnVal = method.invoke(teacherDao ,args); // 执行目标方法
System.out.println("动态代理方法结束");
return returnVal;
}
});
}
}
main(){
TeacherDao dao = new TeacherDao();
ProxyFactory pf = new ProxyFactory(dao);
ITeacherDao proxy = (ITeacherDao) pf.getProxyInstance();
proxy.teach();
/* 控制台输出
进入动态代理方法
调用了 teach 方法,开始教书
动态代理方法结束
*/
}
Cglib代理
通过Cglib获取代理对象
public class ProxyFactory implements MethodInterceptor{
ITeacherDao teacherDao; // 被代理对象
public ProxyFactory(ITeacherDao teacherDao){
this.teacherDao = teacherDao;
}
public Object getProxyInstance(){
// 1. 创建一个工具类
Enhancer enhancer = new Enhancer();
// 2.设置父类
enhancer.setSuperClass(teacherDao.getClass());
// 3.设置回调函数
enhancer.setCallback(this);
// 4.返回代理对象
return enhancer.create();
}
}
// 重写 intercept 方法,会调用目标对象的方法
@Override
public Object intercept(Object arg0, Method method , Object[] args, MethodProxy arg3) throws Throwable{
System.out.println("Cglib代理方法开始执行");
Object returnVal = method(teacherDao, args);
System.out.println("Cglib代理方法结束");
return returnVal;
}