设计模式二(从业务层面)

备注

时间:2020年07月13日
架构课程第三次笔记

一:代理模式

1.1:实现的业务

将图片上传到本地的FastDFS,将视频等大文件上传到阿里OSS。

1.2:业务实现模型图

在这里插入图片描述
讲解:

1、FileUpload抽象接口,定义了文件上传方法,分别给它写了2种实现。
2、AliyunOSSFileUpload是将文件上传到aliyunOSS,主要上传mp4和avi的视频大文件。
3、FastdfsFileUpoad是将文件上传到FastDFS,主要上传png/jpg等图片小文件。 
4、FileUploadProxy是代理对象,供用户访问,调用了FileUpload的文件上传方法,为用户提供不同文件上传调 用。
5、FileController是控制器,用于接收用户提交的文件,并调用代理FileUploadProxy实现文件上传。

1.3:代码实现

  1. 配置文件:
server: 
	port: 18081
	
#FastDFS配置 
fastdfs: 
	url: http://192.168.211.137:28181/ 
	fdfsPrefix: png,jpg #上传前缀配置,默认上传到FastDFS 
	
#aliyun
aliyun: 
 	oss:
 		endpoint: oss-cn-beijing.aliyuncs.com 
 		accessKey: a7i6rVEjbtaJdYX2 
 		accessKeySecret: MeSZPybPHfJtsYCRlEaUbfRtdH8gl4 
 		bucketName: sklll 
 		key: video/ 
 		ossPrefix: avi,mp4 #上传文件前缀配置 
 		backurl: https://sklll.oss-cn-beijing.aliyuncs.com/video/ #访问地址配置 
 	
 spring: 
 	servlet: 
 		multipart: max-file-size: 100MB #上传文件大小配置
  1. FileUpload接口定义:
public interface FileUpload {
	 /*** 
	 * 文件上传 * 
	 * @param buffers:文件字节数组 
	 *  @param extName:后缀名 
	 * @return 
	 */ 
	 String upload(byte[] buffers,String extName);
 }
  1. AliyunOSSFileUpload实现:
@Component(value = "aliyunOSSFileUpload")
public class AliyunOSSFileUpload implements FileUpload {
    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    @Value("${aliyun.oss.accessKey}")
    private String accessKey;
    @Value("${aliyun.oss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${aliyun.oss.key}")
    private String key;
    @Value("${aliyun.oss.bucketName}")
    private String bucketName;
    @Value("${aliyun.oss.backurl}")
    private String backurl;

    /**** 
     * 文件上传 
     * 文件类型如果是图片,则上传到本地FastDFS
     * 文件类型如果是视频,则上传到aliyun OSS 
     */
    @Override
    public String upload(byte[] buffers, String extName) {
        String realName = UUID.randomUUID().toString() + "." + extName;
        // 创建OSSClient实例。 
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKey, accessKeySecret);
        // <yourObjectName>表示上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如 abc/efg/123.jpg。 
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key + realName, new ByteArrayInputStream(buffers));
        // 上传字符串。 
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(FileUtil.getContentType("." + extName));
        putObjectRequest.setMetadata(objectMetadata);
        ossClient.putObject(putObjectRequest);
        // 关闭OSSClient。
        ossClient.shutdown();
        return backurl + realName;
    }
}
  1. FastdfsFileUpoad实现:
@Component(value = "fastdfsFileUpoad")
public class FastdfsFileUpoad implements FileUpload {
    @Value("${fastdfs.url}")
    private String url;

    /*** 
     * 文件上传 
     * @param buffers:文件字节数组 
     * @param extName:后缀名 
     * @return 
     */
    @Override
    public String upload(byte[] buffers, String extName) { /*** * 文件上传后的返回值 * uploadResults[0]:文件上传所存储的组名,例如:group1 * uploadResults[1]:文件存储路径,例如: M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg */
        String[] uploadResults = null;
        try {//获取StorageClient对象 StorageClient storageClient = getStorageClient(); //执行文件上传
            uploadResults = storageClient.upload_file(buffers, extName, null);
            return url + uploadResults[0] + "/" + uploadResults[1];
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*** 
     * 初始化tracker信息 
     */
    static {
        try {
            //获取tracker的配置文件fdfs_client.conf的位置 
            String filePath = new ClassPathResource("fdfs_client.conf").getPath();
            //加载tracker配置信息 
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*** 
     * 获取StorageClient 
     * @return 
     * @throws Exception 
     */
    public static StorageClient getStorageClient() throws Exception {
        //创建TrackerClient对象 
        TrackerClient trackerClient = new TrackerClient();
        //通过TrackerClient获取TrackerServer对象 
        TrackerServer trackerServer = trackerClient.getConnection();
        //通过TrackerServer创建StorageClient 
        StorageClient storageClient = new StorageClient(trackerServer, null);
        return storageClient;
    }
}
  1. FileUploadProxy代理实现:
@Component
public class FileUploadProxy {
    @Autowired
    private FileUpload aliyunOSSFileUpload;
    @Autowired
    private FileUpload fastdfsFileUpoad;
    @Value("${aliyun.oss.ossPrefix}")
    private List<String> ossPrefix;
    @Value("${fastdfs.fdfsPrefix}")
    private List<String> fdfsPrefix;

    /*** 
    * 文件上传代理方法 
    * @return 
    */
    public String upload(byte[] buffers, String extName) {
        //上传至云OSS 
        for (String prefix : ossPrefix) {
            if (extName.equals(prefix)) {
                return aliyunOSSFileUpload.upload(buffers, extName);
            }
        }
        //FastDFS 
        for (String prefix : fdfsPrefix) {
            if (extName.equals(prefix)) {
                return fastdfsFileUpoad.upload(buffers, extName);
            }
        }
        //默认传 FastDFS 
        return fastdfsFileUpoad.upload(buffers, extName);
    }
}
  1. FileController控制器实现:
@RestController
@RequestMapping(value = "/file")
public class FileController {
    @Autowired
    private FileUploadProxy fileUploadProxy;

    /*** 
     * 文件上传 
     * @param file 
     * @return 
     * @throws IOException 
     */
    @PostMapping(value = "/upload")
    public String upload(MultipartFile file) throws IOException {
        return fileUploadProxy.upload(file.getBytes(), StringUtils.getFilenameExtension(file.getOriginalFilename()));
    }
}

二:享元模式

2.1:概述

  1. 概念
有効地支持大量细粒度对象的复用.
  1. 优点和缺点
1:优点
	相同对象只要保存一份,这降低了系统中对象的数量。
2:缺点
	为了使对象可以共享,需要将一些不能共享的属性外部化,增加程序复杂性。

2.2:要实现业务

用户信息共享。

2.3:代码实现

  1. 定义共享组件: LogComponent
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public abstract class LogComponent {
    private String sex;
    private String role;
    private String methodName;
    private String message;

    abstract void supplementLogContent(String... args);

    public LogComponent(String username, String sex, String role) {
        this.username = username;
        this.sex = sex;
        this.role = role;
    }
}
  1. 享元组件逻辑操作对象: SupplementSource
public class SupplementSource extends LogComponent {
    /**** 
     * 填充参数 
     * @param username 
     */
    public SupplementSource(String username, String sex, String role) {
        super(username, sex, role);
    }

    /**** 
     * 业务逻辑,完善不同方法的日志记录 
     * @param args 长度为2,第1个是方法名字,第2个是方日志信息 
     */
    @Override
    void supplementLogContent(String... args) {
        super.setMethodName(args[0]);
        super.setMessage(args[1]);
    }
}
  1. 多线程安全控制: ThreadUserLog
@Component
public class ThreadUserLog {
    
    //存储线程对应的用户名日志信息 
    private static ThreadLocal<LogComponent> userRecode = new ThreadLocal<LogComponent>();

    /****添加用户信息记录 */
    public void add(LogComponent logComponent) {
        userRecode.set(logComponent);
    }

    /*** * 记录方法名和参数*/
    public String reload(String... args) {
        //获取对象 
        LogComponent logComponent = userRecode.get();
        //设置数据 
        logComponent.supplementLogContent(args);
        return logComponent.toString();
    }

    /****获取LogComponent */
    public LogComponent get() {
        return userRecode.get();
    }

    /**** * 移除 */
    public void remove() {
        userRecode.remove();
    }
}
  1. 线程会话初始化: AuthorizationInterceptor
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
    @Autowired
    private ThreadUserLog threadUserLog;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            //获取令牌
            String authorization = request.getHeader("Authorization");
            // 解析令牌
            if (!StringUtils.isEmpty(authorization)) {
                Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);
                //将用户数据存入到ThreadLocal中
                LogComponent logComponent = new SupplementSource(tokenMap.get("username").toString(), 
                        tokenMap.get("sex").toString(),
                        tokenMap.get("role").toString());
                //添加当前线程用户信息记录
                threadUserLog.add(logComponent);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //输出令牌校验失败
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().
                print("身份校验失败!");
        response.getWriter().
                close();
        return false;
    }

    /*** 移除会话信息 * @throws Exception */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        threadUserLog.remove();
    }
}
  1. 配置拦截器
@Component
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private AuthorizationInterceptor authorizationInterceptor;

    /*** * 拦截器配置 */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login");
    }
}

三:装饰者模式

3.1:概述

  1. 定义
不改变类结构的前提下,动态向一个对象中添加新的功能。
  1. 优缺点
1:优点
	装饰类和被装饰类可以独立发展不耦合。
2:缺点
	多层装饰比较复杂。

3.2:业务场景

  1. 场景
价格计算(订单价格,结算价格)
  1. 思路分析
1、创建接口(MoneyOperation),定义订单价格计算,因为所有价格波动,都是基于订单价格来波动的。
2、创建订单价格计算类(OrderPayMoneyOperation),实现MoneyOperation接口,实现订单价格计算。 
3、创建装饰者对象(Decorator),以供功能扩展。 
4、实现优惠券优惠金额计算功能扩展,创建Decorator的扩展类CouponsMoneyOperation,先计算订单金额,再 计算优惠券使用之后的优惠金额。 
5、实现金币抵现功能扩展,创建Decorator的扩展类GoldMoneyOperation,先计算订单金额,再实现金币优惠之 后的金额。

3.3:代码实现

  1. 创建接口 MoneyOperation
public interface MoneyOperation { 
	/*** * 订单金额计算 * @return */ 
	Integer operation(Order order); 
}
  1. :创建类 OrderPayMoneyOperation 实现订单金额的计算
@Component
public class OrderPayMoneyOperation implements MoneyOperation {
    @Autowired
    private ItemDao itemDao;
    /*** * 订单金额计算 */
    @Override
    public Integer operation(Order order) {
        //根据ID查询商品 
        Item item = itemDao.findById(order.getItemId());
        //计算订单金额 
        return item.getPrice() * order.getNum();
    }
}
  1. 创建装饰者类 Decorator 供其他类扩展。
public abstract class Decorator implements MoneyOperation {
    private MoneyOperation moneyOperation;

    public void setMoneyOperation(MoneyOperation moneyOperation) {
        this.moneyOperation = moneyOperation;
    }

    /*** * 订单金额计算 */
    @Override
    public Integer operation(Order order) {
        return moneyOperation.operation(order);
    }
}
  1. 创建类 CouponsMoneyOperation 扩展装饰者类,实现优惠券优惠价格计算
@Component
public class CouponsMoneyOperation extends Decorator {
    @Autowired
    private ThreadUserLog threadUserLog;
    @Autowired
    private CouponsDao couponsDao;

    /*** * 功能扩展,增加优惠券折扣计算 */
    @Override
    public Integer operation(Order order) {
        //订单金额计算 
        Integer payMoney = super.operation(order);
        //优惠券金额优惠后金额计算 
        payMoney = couponsMoney(payMoney, order.getCouponsId());
        return payMoney > 0 ? payMoney : 0;
    }

    /*** * 优惠券折扣计算 */
    public Integer couponsMoney(Integer payMoney, String couponsId) {
        if (payMoney <= 0) {
            return 0;
        }
        //获取用户username->结合了享元模式 
        LogComponent logComponent = threadUserLog.get();
        //查询优惠券 
        Coupons coupons = couponsDao.findByIdAndUserName(couponsId, logComponent.getUsername());
        if (coupons == null) {
            return payMoney;
        }
        //价格计算 
        payMoney = payMoney - coupons.getMoney();
        //标记优惠券 
        couponsDao.modifyCouponsStatus(couponsId);
        return payMoney > 0 ? payMoney : 0;
    }
}
  1. 创建类 GoldMoneyOperation ,实现金币抵现优惠计算。
@Component
public class GoldMoneyOperation extends Decorator {
    @Autowired
    private ThreadUserLog threadUserLog;
    @Autowired
    private UserDao userDao;

    /*** * 扩展功能:金币优惠后计算 */
    @Override
    public Integer operation(Order order) {
        //订单基本金额计算 
        Integer payMoney = super.operation(order);
        //金币优惠 
        payMoney = gold(payMoney);
        return payMoney;
    }

    /*** * 金币优惠计算 */
    public Integer gold(Integer payMoney) {
        if (payMoney <= 0) {
            return 0;
        }
        //获取当前用户的会话->结合了享元模式 
        LogComponent logComponent = threadUserLog.get();
        User user = userDao.findByUserName(logComponent.getUsername());
        if (user.getGold() <= 0) {
            return payMoney;
        }
        //剩余金币 
        int remaining = 0;
        //金币比payMoney小 
        if (payMoney >= user.getGold()) {
            payMoney = payMoney - user.getGold();
        } else {
            remaining = user.getGold() - payMoney;
            payMoney = 0;
        }
        //更新用户剩余金币 
        userDao.modifyGold(logComponent.getUsername(), remaining);
        return payMoney;
    }
}

四:工厂模式

4.1:概述

  1. 定义
提供一个工厂类获取对象。
  1. 优缺点
1:优点
	解耦,降低对象之间的依赖。
2:缺点
	每次增加一个新的产品,都需要重新创建一个工厂。

4.2:业务场景

支付渠道选择。

4.3:代码实现

  1. PayChannel 定义支付行为。
public interface PayChannel { 
	/*** * 支付 */ 
	void pay(); 
}
  1. WeixinPay 实现微信支付操作
@Component("weixinPay")
public class WeixinPay implements PayChannel {
    @Override
    public void pay() {
        System.out.println("微信支付成功!");
    }
}
  1. AliPay 实现支付宝支付
@Component("aliPay")
public class AliPay implements PayChannel {
    @Override
    public void pay() {
        System.out.println("支付宝支付成功!");
    }
} 
  1. 支付渠道映射配置
#支付通道列表,application.yml
pay:
	paymap: {"1":"weixinPay","2":"aliPay"}
  1. 创建 PayFactory 用于获取支付渠道的实例
@Data
@Component
@ConfigurationProperties(prefix = "pay")
public class PayFactory implements ApplicationContextAware {
    //Spring容器 
    private static ApplicationContext applicationContext;
    //支付键值对信息 
    private Map<String, String> paymap;

    /*** * 创建支付通道,从paymap中获取对应通道的实例名字,从applicationContext获取通道实例 */
    public PayChannel createChannel(String key) {
        return applicationContext.getBean(paymap.get(key), PayChannel.class);
    }

    /*** * 获取容器 * @param applicationContext * @throws BeansException */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        PayFactory.applicationContext = applicationContext;
    }
}

五:状态模式

5.1:概述

5.2:业务场景

5.3:代码实现

  1. State 接口,用于定义更新状态对象,同时执行相关的行为。
public interface State {
    /*** * 变更状态 * @param order */
    void doAction(Order order);

    /*** * 执行行为 */
    void execute();
}
  1. SendMsgBehavior(成功)
@Component("sendMsgBehavior")
public class SendMsgBehavior implements State {
    @Override
    public void doAction(Order order) {
        System.out.println("订单支付");
        order.setState(this);
    }

    @Override
    public void execute() {
        System.out.println("订单变更为已支付,需要通知商家发货!");
    }
}
  1. ResetStoreBehavior(失败)
@Component("resetStoreBehavior")
public class ResetStoreBehavior implements State {
    @Override
    public void doAction(Order order) {
        System.out.println("订单取消");
        order.setState(this);
    }

    @Override
    public void execute() {
        System.out.println("订单取消,执行库存回滚!");
        System.out.println("订单取消,执行退款!");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值