17.1 装饰者模式
以上IO实例中使用了一个非常经典的设计模式,装饰者设计模式,解决是类在不使用继承的情况下对类的功能进行扩展。
以奶茶的案例来讲解
奶茶店现在逐步都支持了线上点餐的方式,我们现在的目标就是实现一个简单的奶茶选择搭配生成对应搭配的类型选择结果与价格的一个功能。
我们都知道点一杯奶茶的使用的订单系统,基本如下:
(1)选择奶茶类型例如:乌龙奶茶 10 wulong 芋圆奶茶 12 yuyuan 可可奶茶 8 keke 波波奶茶 15 bobo。
点击图片之后进入选择对应奶茶的规格配置。
(2)选择份量:小杯、大杯
不同份量对应价格不同。
(3)选择配料内容例如:加燕麦(yanmai)、加椰果(yeguo)、加仙草(xiancao)、红豆(hongdou)。
其中配料一次可以选中多个,每选择一个都会对价格进行增加
(4)选择甜度:全糖、7分糖、3分糖、微糖
(5)选择温度:正常冰、去冰、温热
在选择完配置以后,总计会显示:
(1)已选规格:招牌芋圆奶茶、大杯、加椰果、3分糖、去冰
(2)奶茶价格:18 实现这个案例直接想到的方式就是继承:
加配料这时候也可以采用建类继承的形式
这里类很多是不必要实现的,可以通过实例变量和继承的方式来写,因此有人提出,我们可以把这些调料放到基类Beverage饮料类中去。加上实例变量布尔值Boolean代表是否加入调料。那么设计方式可能是这样的:
这样设计的方式有什么不好的地方?
(1)扩展性不好:如果出现新的配料,需要修改Beverage中的字段值,并且改变超类中的cost的计算方式(需要遍历所有的成员函数判断是否有这个配料)。
(2)会出现不必要的对象继承:如果开发出了一款果茶的饮料,我们继续去继承Beverage的类,那么会把燕麦的成员变量与方法hasYanmai继承下来。
装饰者模式来解决这一问题:
1、被装饰者父类
2、被装饰者
3、装饰者的父类-->过度器
4、装饰者
代码语言:javascript复制package org.qf.entitys;
//被装饰的父类
public interface Beverage {
//打印奶茶信息
public String getDesc();
//计算价格
public double cost();
}
package org.qf.entitys;
//被装饰的类
public class TeaMilk implements Beverage {
@Override
public String getDesc() {
return "奶茶";
}
@Override
public double cost() {
return 4;
}
}
//装饰者的父类
public abstract class FilterBeverage implements Beverage {
private Beverage teaMilk;
public FilterBeverage(Beverage teaMilk){
this.teaMilk=teaMilk;
}
@Override
public String getDesc() {
return teaMilk.getDesc();
}
@Override
public double cost() {
return teaMilk.cost();
}
}
//修饰者类芋圆
package org.qf.entitys;
public class YuYuanTeaMilk extends FilterBeverage{
public YuYuanTeaMilk(Beverage teaMilk) {
super(teaMilk);
}
@Override
public String getDesc() {
return super.getDesc() " 芋圆";
}
@Override
public double cost() {
return super.cost() 1;
}
}
//装饰者类之珍珠
package org.qf.entitys;
public class ZhenZhuTeaMilk extends FilterBeverage{
public ZhenZhuTeaMilk(Beverage teaMilk) {
super(teaMilk);
}
@Override
public String getDesc() {
return super.getDesc() " 珍珠";
}
@Override
public double cost() {
return super.cost() 2;
}
}
//装饰者之果冻
package org.qf.entitys;
public class GuoDongTeaMilk extends FilterBeverage{
public GuoDongTeaMilk(Beverage teaMilk) {
super(teaMilk);
}
@Override
public String getDesc() {
return super.getDesc() " 果冻";
}
@Override
public double cost() {
return super.cost() 3;
}
}
装饰者模式就是用一个又一个的修饰者类去对基础服务类进行装饰。
17.2 单例模式
单例模式 一个类只允许创建一个对象,这就是单例模式,以后项目中有很多的类属于功能型的类,这种功能型的类不需要每次使用都去新建一个对象,只有可能只需要创建一个对象就能整个项目中使用,这时就要引入单例模式。
1、将一个类的构造私有化
代码语言:javascript复制public class Student {
private Student(){}
}
2、单例模式有两种
- 2.1 懒汉式
- 2.2 饿汉式
构造方法私有化之后,需要提供一个能够获取本对象的方法
代码语言:javascript复制public class Student {
private Student(){}
public static Student getInstance(){
}
}
3、在本类中定义一个本类对象为静态属性
代码语言:javascript复制public class Student {
private Student(){}
private static Student student;
}
4.1、懒汉式的实现:
在获取对象的时候再对,对象进行实例化,这就是懒汉式
代码语言:javascript复制public class Student {
private Student(){}
private static Student student;
public static Student getInstance(){
if(student==null){
student=new Student();
}
return student;
}
}
4.2、 饿汉式的实现:
在定义这个对象时就将对象实现化,这就是饿汉式
代码语言:javascript复制public class Student {
private Student(){}
private static Student student=new Student();
public static Student getInstance(){
return student;
}
}
在多线程中的单例:
代码语言:javascript复制package com.qf.demo1;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.locks.ReentrantLock;
public class Student {
private int id;
private String name;
private int age;
//使用当前本类的对象作为一个静态属性存在
private static Student student;
private static ReentrantLock lock=new ReentrantLock();
private Student(){}
public static Student newInstance(){
//在多线程中单例前面几次线程的争抢会导致单例失败
if(student==null){
try{
//使用锁来对线程的争抢进行限制
lock.lock();
//但是前面的几个线程已经绕开了第一个判断,虽然上面进行上锁,当执行完成下面的创建代码以后,释放锁后依然会再次执行创建代码
//有点类似于虚假唤醒,所以在这里进行两次判断对线程进行条件筛选。
if(student==null) {
student = new Student();
}
}finally {
lock.unlock();
}
}
return student;
}
}
代码语言:javascript复制public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 10; i ) {
new Thread(()->{
System.out.println(Student.newInstance());
}).start();
}
}
}
作用:
1、这样的设计节约资源空间
2、为什么要节约,在项目中很多的功能类对象,真的只需要一个就行。
17.3 简单工厂
这个设计模式也是用于创建对象使用
创建一个工厂类,专门用于对象的创建
写一个静态的创建方法,根据传入的标志来创建不同的对象,利用多态的原理
代码语言:javascript复制public class CarFactory {
public static Car createCar(String type){
switch (type.toLowerCase()){
case "benz":
return new BenZ();
case "bwm":
return new BWM();
case "aodi":
return new Aodi();
default:
throw new RuntimeException("没有这个对象");
}
}
}
代码语言:javascript复制package com.simplefactory.entitys;
public abstract class Car {
public Car(String name, double price, String model) {
this.name = name;
this.price = price;
this.model = model;
}
public Car() {
}
private String name;
private double price;
private String model;
public abstract void driver();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
代码语言:javascript复制package com.simplefactory.entitys;
public class BWM extends Car {
@Override
public void driver() {
System.out.println(super.getName() "说:奔驰,奥迪都是弟弟");
}
}
package com.simplefactory.entitys;
public class BenZ extends Car {
@Override
public void driver() {
System.out.println(super.getName() "身价:" super.getPrice());
}
}
package com.simplefactory.entitys;
public class Aodi extends Car {
@Override
public void driver() {
System.out.println(super.getName() super.getModel() "行走中的" super.getPrice());
}
}
代码语言:javascript复制public class Demo1 {
public static void main(String[] args) {
Car car=CarFactory.createCar("aodi");
car.setName("奥迪");
car.setModel("A6L");
car.setPrice(500000);
car.driver();
Car bwm=CarFactory.createCar("bwm");
bwm.setName("宝马");
bwm.setModel("740");
bwm.setPrice(1000000);
bwm.driver();
}
}
17.4 工厂方法
OOP 开-闭原则:对扩展开放对修改关闭。功能的扩展要开放,如果对功能的扩展进行时要修改以前的代码这种操作要关闭。
代码语言:javascript复制package com.simplefactory;
import com.simplefactory.entitys.*;
public abstract class CarFactory {
public abstract Car createCar();
}
package com.simplefactory;
import com.simplefactory.entitys.Aodi;
import com.simplefactory.entitys.Car;
public class AodiFactory extends CarFactory {
@Override
public Car createCar() {
return new Aodi();
}
}
package com.simplefactory;
import com.simplefactory.entitys.BenZ;
import com.simplefactory.entitys.Car;
public class BenZFactory extends CarFactory {
@Override
public Car createCar() {
return new BenZ();
}
}
package com.simplefactory;
import com.simplefactory.entitys.BWM;
import com.simplefactory.entitys.Car;
public class BWMFactory extends CarFactory {
@Override
public Car createCar() {
return new BWM();
}
}
//新扩展的长安汽车工厂类
package com.simplefactory;
import com.simplefactory.entitys.Car;
import com.simplefactory.entitys.ChangAn;
public class ChangAnFactory extends CarFactory {
@Override
public Car createCar() {
return new ChangAn();
}
}
//新扩展的长安汽车类
package com.simplefactory.entitys;
public class ChangAn extends Car {
@Override
public void driver() {
System.out.println("我是" super.getName() ",我得风快");
}
}
//调用类,工厂方法是将对象创建的主动权回交给用户处,让用户来决定到底要创建哪个对象
import com.simplefactory.entitys.Car;
public class Demo1 {
public static void main(String[] args) {
//先创建工厂对象
CarFactory carFactory=new ChangAnFactory();
//再由工厂对象获取对应的实际对象
Car bwm=carFactory.createCar();
bwm.setName("长安");
bwm.driver();
}
}
17.5 策略模式
策略模式用于替换掉简单工厂中的选择判断结构。
Context是一个策略类,在策略类列举出所有要创建的对象,通过Context(上下文) 类中的Map来进行承接所有对象的存储,然后根据指定的type(策略)去策略集中寻找对应的对象。
代码语言:javascript复制import java.util.HashMap;
import java.util.Map;
public class Context {
//策略 策略的存在
private static Map<String,CarFactory> factoryMap=new HashMap<>();
static {
//事先将所有需要的类创建出来存入map 这是一种饿汉式,简单工厂中根据判断再创建对象是不是一种懒汉式
factoryMap.put("benz",new BenZFactory());
factoryMap.put("bwm",new BWMFactory());
factoryMap.put("aodi",new AodiFactory());
factoryMap.put("changan",new ChangAnFactory());
}
public static CarFactory createFactory(String type){
//标识与我的策略里寻找对应的值 这样将选择判断结构替换
return factoryMap.get(type.toLowerCase());
}
}
代码语言:javascript复制public class Demo1 {
public static void main(String[] args) {
CarFactory carFactory=Context.createFactory("changan"); //从策略类中获取对象
Car bwm=carFactory.createCar();
bwm.setName("长安");
bwm.driver();
}
}
17.6 静态代理
代理,有一件事情找人帮忙去做,去做的这个人理解为代理人。SpringAOP中用到的模式,本身不直接操作原对象,使用代理对象去操作原对象中的方法,这样代理对象就对可以原方法进行前后的增强。
代码语言:javascript复制package com.proxyDemo;
public interface Calc {
//要做的事情
public Integer calc(int n);
public Integer mul(int n);
}
//原计算类
public class CalcNum implements Calc {
//原计算方法
@Override
public Integer calc(int n) {
int sum=0;
for (int i = 1; i <= n ; i ) {
if(i%5==0) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sum =i;
}
return sum;
}
public Integer mul(int n){
int sum=1;
for(int i=1;i<=n;i ){
sum*=i;
}
return sum;
}
}
如果现在在不修改 代码的情况下对原计算方法扩展判断参数不能是负数和计算过程时间统计的功能怎么办?
使用静态代理类来完成。
代码语言:javascript复制package com.proxyDemo;
public class CalcProxy implements Calc {
private Calc target;
public CalcProxy(Calc target){
this.target=target;
}
@Override
public Integer calc(int n) {
//以下开始结束部分的代码都是在对原方法进行功能上的扩充
//开始
if(n<0){
throw new RuntimeException("参数不能是负数");
}
long start = System.currentTimeMillis();
//结束
Integer result = this.target.calc(n);
//开始
long end = System.currentTimeMillis();
System.out.println(end-start);
//结束
return result;
}
public Integer mul(int n){
.....
}
}
代码语言:javascript复制package com.proxyDemo;
public class Demo1 {
public static void main(String[] args) {
CalcNum calcNum=new CalcNum();//原方法所在类的对象
CalcProxy proxy=new CalcProxy(calcNum);//代理类对象
System.out.println(proxy.calc(100));//不直接操作原方法,使用第三方的代理去操作
proxy.nul(10);
}
}
17.7 适配器模式
一台电脑,电脑的投影接口是一个VGA接口,只能连接VGA接口的投影,这台电脑换了一个地方后,那个地方是一个HDMI接口的投影,如果这台电脑想要连接HDMI接口的投影时怎么办呢,如果一个转接头,转接头的屁股连接HDMI的连接线,转接头的头是一个VGA这样的接口。这样就能完成HDMI投影到VGA接口电脑的连接。
代码语言:javascript复制//VGA接口
package com.adapterDemo;
public interface VGA {
public void inputData();
}
//VGA投影仪
package com.adapterDemo;
public class VGAProjector implements VGA {
@Override
public void inputData() {
System.out.println("VGA接口开始传输数据,播放电脑画面");
}
}
//这台电脑在生产的时候就没有考虑过HDMI接口的存在
package com.adapterDemo;
public class Computer {
//所以这台电脑在投影连接方法这里只保留了VGA对象参数
public void connect(VGA vga){
System.out.print("开始安装驱动");
for (int i = 0; i < 6; i ) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("n驱动安装完成");
vga.inputData();
}
}
//调用代码
package com.adapterDemo;
public class Demo {
public static void main(String[] args) {
Computer computer=new Computer();
VGA vga=new VGAProjector();
computer.connect(vga);
}
}
运行结果:
开始安装驱动......
驱动安装完成
VGA接口开始传输数据,播放电脑画面
以上代码完成正常执行没有问题逻辑也很简单,现在市场上出现一种高清连接口的投影叫做HDMI
代码语言:javascript复制//高清接口
package com.adapterDemo;
public interface HDMI {
public void inputDataHight();
}
//高清接口的投影仪
package com.adapterDemo;
public class HDMIProjector implements HDMI {
@Override
public void inputDataHight() {
System.out.println("HDMI接口开始传输数据,播放电脑高清画面");
}
}
现在这台电脑在不违反开闭原则的情况,如何将HDMI接口的投影连接上去?
现在的连接只差一个转换头(适配器类)
代码语言:javascript复制//适配器类
package com.adapterDemo;
//实现VGA 接口,让当前的这个类能够满足多态,
public class VGAAdapter implements VGA {
//放一个HDMI对象
public HDMI hdmi;
public VGAAdapter(HDMI hdmi) {
this.hdmi = hdmi;
}
//这个适配类的数据传输不做具体的事,只能是将数据过渡了一下
@Override
public void inputData() {
//使用HDMI调用高清功能。
this.hdmi.inputDataHight();
}
}
代码语言:javascript复制//调用类
package com.adapterDemo;
public class Demo {
public static void main(String[] args) {
Computer computer=new Computer();
//高清投影机
HDMI hdmi=new HDMIProjector();
//hdmi和vga两者类型并不一样,不能直接传 , 中间使用转换器
VGA vgaAdapter=new VGAAdapter(hdmi);
//连接的时候将转换器连接到电脑上
computer.connect(vgaAdapter);
}
}
运行结果:
开始安装驱动......
驱动安装完成
HDMI接口开始传输数据,播放电脑高清画面
17.8 迭代器模式
1、Iterator迭代器接口
2、业务类也需要一个接口,这个接口用于定义获取迭代器的对象
3、需要一个内部类去实现Iterator接口重写hashNext和Next方法
4、使用foreach进行迭代循环
代码语言:javascript复制//获取迭代器的接口
public interface Container<E> extends Iterable<E> {
}
代码语言:javascript复制//迭代器类
package com.qf.demo5;
import java.util.Iterator;
import java.util.List;
public class Ntr<E> implements Iterator<E> {
private List<E> objs;
private int index=0;
public Ntr(List<E> objs){
this.objs=objs;
}
@Override
public boolean hasNext() {
return index<objs.size();
}
@Override
public E next() {
Object obj= objs.get(index);
index ;
return (E)obj;
}
}
代码语言:javascript复制//业务类,要实现迭代器获取接口
package com.qf.demo5;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class NameRepository<E> implements Container<E> {
private List<String> names=new ArrayList<>();
public NameRepository(){
names.add("张三");
names.add("李四");
names.add("王五");
names.add("朱六");
}
@Override
public Iterator iterator() {
return new Ntr<String>(names);
}
}
代码语言:javascript复制//调用类
package com.qf.demo5;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
NameRepository<String> nr=new NameRepository<>();
Iterator iterator = nr.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
18.1 结语
在此小龙谢谢大家的支持,我也会更用心的去更新文章!