重构代码之美

  1. 什么是重构;
  2. 为什么要重构;
  3. 什么时候重构;
  4. 怎样避免重构的现象;
  5. 重构的难点在哪里;
  6. 如何来实施代码重构;
  7. 重构如何灵活的使用设计模式(实战演练中讲解);
  8. 重构时错误的设计模式使用范例(实战演练中讲解);
  9. 实战演练(含代码讲述)。

###什么是重构?

  1. 代码优化;
  2. 重构就是通过调整程序代码,再不改变其功能基础之上,改善代码的可读性,提高代码的可扩展性。

为什么要重构?

基本上我们开发的时候,产品天天改需求,而且每次改动的需求可能会导致很多的代码实现要重新修改,没办法我们只能继续安静的去分析需求,很多时候很快代码就敲出来了,但是呢,开始构建代码的时候我们很多时候没有考虑到代码结构是否 OK?是否要调整了?是否需要优化?会不会出现很不可思议的 bug?(还有一个是我自己觉得的,就是我感觉重构之后的代码看起来很简洁、清楚,而且很装逼!哈哈)

总结:需求的共性,有什么共同特点 (预知性),总之从自身找原因,宁可花很长的时间去思考,也不要短时间内去开发。

###什么时候需要重构?

  1. 发生在换领导,一般都会重构;
  2. 我们需求变更非常有规律的时候,你要考虑重构;
  3. 当你发现你的代码过度耦合;
  4. N 个相同的类名去实现很多重复的功能,沟通,需要重构,把各个有点整合在一起;
  5. 不完美的设计;不合理,不可扩展,一次性用品;
  6. 缺少注释的代码,极度需要重构。

怎样避免这样的现象

  1. 分析需求:对拿到的需求进行分析;
  2. 预见性:模拟需求现实场景,对开发程序思路进行组合,是否能走通流程,是不是有相同的地方等;
  3. 多沟通:多和产品沟通。

###重构的难点在哪里?

  1. 数据库 Dao 端的改造,数据迁移;
  2. 代码结构耦合性太高,逻辑非常复杂,可能改一个地方错 N 个地方;
  3. 面向接口编程(别人调用的时候可能出错)。

###如何来实施代码重构

  1. 提取类、抽离方法(代码复用);
  2. 分离复杂的逻辑判断条件;
  3. 引用参数对象,保留全局变量;
  4. 用符号常量替换无意义的数字;
  5. 重名名方法(提高可读性);
  6. 内存优化(需要的时候才 new 对象,不要 new 出来而不使用);
  7. 去除硬编码,将文案统一到模块化的宏定义文件(抽离了多少个常量,用 word 给它记录下来,后面有据可查,要是那个地方错了,我们可以很快的定位到错误的地方);
  8. 可在适合的场景下用合适的设计模式。

注:推荐大家可以看看《重构改善既有代码的设计》

实战演练

####创建普通的 maven 项目,导入以下 jar 包

 <dependencies>
  <!--数据库连接驱动 -->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.13</version>
  </dependency>

  <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
  <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.54</version>
  </dependency>
 </dependencies>

####建立商品实体对象

package com.restructure.demo;

import java.io.Serializable;

/**
 * 商品
 */
public class ShopDO implements Serializable {

    private static final long serialVersionUID = 5370587994637698136L;

    /**
     *  商品ID
     */
    private Long id;

    /**
     *  商品名称
     */
    private String shopName;

    /**
     *  商品价格
     */
    private Long shopPrice;

    /**
     *  商品可售库存
     */
    private Integer number;

    /**
     * 出货口
     */
    private Integer port;

    /**
     * 出货口状态
     */
    private Integer portStatus;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getShopName() {
        return shopName;
    }

    public void setShopName(String shopName) {
        this.shopName = shopName;
    }

    public Long getShopPrice() {
        return shopPrice;
    }

    public void setShopPrice(Long shopPrice) {
        this.shopPrice = shopPrice;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public Integer getPortStatus() {
        return portStatus;
    }

    public void setPortStatus(Integer portStatus) {
        this.portStatus = portStatus;
    }
}
建立数据库连接驱动帮助类 1.0.0 版本
package com.restructure.demo.demo1.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DBHelper1 {

    public static final String url = "jdbc:mysql://localhost:3306/restructure?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true";
    public static final String name = "com.mysql.cj.jdbc.Driver";
    public static final String user = "root";
    public static final String password = "root";

    public Connection conn = null;
    public PreparedStatement pst = null;

    public DBHelper1(String sql) {
        try {
            //指定连接类型
            Class.forName(name);
            //获取连接
            conn = DriverManager.getConnection(url, user, password);
            //准备执行语句
            pst = conn.prepareStatement(sql);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() {
        try {
            this.conn.close();
            this.pst.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

####编写实现类

package com.restructure.demo.demo1;

import com.restructure.demo.demo1.common.DBHelper1;
import com.restructure.demo.ShopDO;

import java.sql.ResultSet;

/**
 * 自动售卖机
 */
public class VendingMachine1 {

    private static DBHelper1 dBHelper1 =null;

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        vendingMachine("哈比",10L,2);
    }

    /**
     * 得到商品
     * @param userName 用户
     * @param price    钱
     * @param port     出货口
     * @return
     */
    public static void vendingMachine(String userName, long price, int port) throws Exception {
        //获取商品
        ShopDO shopDO=getShopByPort(port);
        //判断价格 & 库存
        if(shopDO==null || price < shopDO.getShopPrice() || shopDO.getNumber() <=0){
            System.out.println("该出货口暂无商品可销售");
            return;
        }
        //扣减库存
        deductionShopById(shopDO.getId());

        ShopDO shopDO1=getShopByShopId(shopDO.getId());
        System.out.println("==================================================");
        System.out.println("购买用户:"+userName);
        System.out.println("投入金额:"+price+"元");
        System.out.println("出 货 口:"+port);
        System.out.println("购买商品:"+shopDO.getShopName());
        System.out.println("商品价格:"+shopDO.getShopPrice()+"元");
        System.out.println("找    零:"+(price-shopDO.getShopPrice())+"元");
        System.out.println("原 库 存:"+shopDO.getNumber());
        System.out.println("剩余库存:"+shopDO1.getNumber());
        System.out.println("==================================================");
    }


    public static ShopDO getShopByPort(int port) throws Exception {
        ShopDO shopDO=new ShopDO();
        String sql = "SELECT * FROM shop WHERE port_status =1 and port ="+port;
        dBHelper1 = new DBHelper1(sql);
        ResultSet ret = dBHelper1.pst.executeQuery();
        while (ret.next()) {
            shopDO.setId(Long.parseLong(ret.getString("id")));
            shopDO.setShopName(ret.getString("shop_name"));
            shopDO.setShopPrice(Long.parseLong(ret.getString("sale_price")));
            shopDO.setNumber(Integer.parseInt(ret.getString("number")));
            shopDO.setPort(Integer.parseInt(ret.getString("port")));
            shopDO.setPortStatus(Integer.parseInt(ret.getString("port_status")));
        }
        ret.close();
        dBHelper1.close();//关闭连接
        return  shopDO;
    }

    public static ShopDO getShopByShopId(Long  shopId) throws Exception {
        ShopDO shopDO=new ShopDO();
        String sql = "SELECT * FROM shop where id="+shopId;
        dBHelper1 = new DBHelper1(sql);
        ResultSet ret = dBHelper1.pst.executeQuery();
        while (ret.next()) {
            shopDO.setId(Long.parseLong(ret.getString("id")));
            shopDO.setShopName(ret.getString("shop_name"));
            shopDO.setShopPrice(Long.parseLong(ret.getString("sale_price")));
            shopDO.setNumber(Integer.parseInt(ret.getString("number")));
            shopDO.setPort(Integer.parseInt(ret.getString("port")));
            shopDO.setPortStatus(Integer.parseInt(ret.getString("port_status")));
        }
        ret.close();
        dBHelper1.close();//关闭连接
        return  shopDO;
    }

    public static boolean deductionShopById(Long shopId) throws Exception {
        String sql = "UPDATE shop SET number = number-1 WHERE id ="+shopId;
        dBHelper1 = new DBHelper1(sql);
        int ret = dBHelper1.pst.executeUpdate();
        //关闭连接
        dBHelper1.close();
        return  ret > 0;
    }

}
思路分析
  1. 代码分析:
  • 方法名上没有注释;
  • 有重复代码;
  • 方法利的对象[ShopDO]不是在该使用的时候创建的;
  • vendingMachine 方法功能不够单一;
  • 连接数据库的帮助类里面的常量可已提取出来;
  • 结构是否合理。
  1. 实施方案:
  • 加上注释(方便以后阅读);
  • 提取代码重复(代码复用);
  • 规范代码创建顺序;
  • 执行单一原则(单纯的处理某个业务);
  • 常量类是不是可以提取出来公共维护;
  • 考虑结构的合理性;
  • 是不是可以考虑设计模式、或者加入抽象类等方法来进行编写。
代码优化(一)
  1. 实体类不变
  2. 更新数据库连接帮助类实现
package com.restructure.demo.demo2.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.sql.*;

public class DBHelper2 {

    private Connection conn = null;
    private PreparedStatement pst = null;
    private ResultSet rst = null;

    /**
     * 建立数据库连接
     * @return 数据库连接
     */
    public Connection getConnection() {
        try {
            // 加载数据库驱动程序
            try {
                Class.forName(SQLCommon.driver);
            } catch (ClassNotFoundException e) {
                System.out.println("加载驱动错误");
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
            // 获取连接
            conn = DriverManager.getConnection(SQLCommon.url, SQLCommon.user, SQLCommon.password);
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
        return conn;
    }


    /**
     * insert update delete SQL语句的执行的统一方法
     * @param sql SQL语句
     * @return 执行是否成功
     */
    public boolean executeUpdate(String sql) {
        try {
            // 获得连接
            conn = this.getConnection();
            // 调用SQL
            pst = conn.prepareStatement(sql);
            // 执行
            return pst.executeUpdate() > 0;
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        } finally {
            // 释放资源
            close();
        }
        return false;
    }

    /**
     * SQL 查询将查询结果:一行一列
     * @param sql SQL语句
     * @return 结果集
     */
    public Object findOne(String sql,Class cz) {
        try {
            // 获得连接
            conn = this.getConnection();
            // 调用SQL
            pst = conn.prepareStatement(sql);
            // 执行
            rst = pst.executeQuery();
            ResultSetMetaData data=rst.getMetaData();
            JSONObject jsonObject=new JSONObject();
            while (rst.next()){
                for (int i = 1; i <= data.getColumnCount(); i++){
                    //获取列字段
                    String fieldName = data.getColumnName(i);
                    jsonObject.put(fieldName,rst.getString(fieldName));
                }
            }
            //JSON转对象
            return JSON.parseObject(jsonObject.toJSONString(),cz);
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        } finally {
            close();
        }
        return null;
    }


    public void close() {
        // 关闭结果集对象
        if (rst != null) {
            try {
                rst.close();
            } catch (SQLException e) {
                System.out.println(e.getMessage());
            }
        }

        // 关闭PreparedStatement对象
        if (pst != null) {
            try {
                pst.close();
            } catch (SQLException e) {
                System.out.println(e.getMessage());
            }
        }
        // 关闭Connection 对象
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

3、抽取 SQL 为公共数据(也可以不用,毕竟该实例比较小,不过我们为了规范还是抽取先)

package com.restructure.demo.demo2.common;

public class SQLCommon {

    public static final String url = "jdbc:mysql://localhost:3306/restructure?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true";
    public static final String driver = "com.mysql.cj.jdbc.Driver";
    public static final String user = "root";
    public static final String password = "root";

}

4、修改项目结构,加入 mapper 类——执行单一原则,只管底层数据连接,不管业务

package com.restructure.demo.demo2.dao;

import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.common.DBHelper2;

/**
 * 底层数据处理
 */
public class ShopMapper {


    /**
     * 根据出货口查询可售商品信息
     * @param port 出货口
     * @return 可售商品信息
     * @throws Exception
     */
    public ShopDO getShopByPort(int port){
        DBHelper2 dBHelper2 =new DBHelper2();
        String sql = "SELECT * FROM shop WHERE port_status =1 and port ="+port;
        return  (ShopDO) dBHelper2.findOne(sql,ShopDO.class);
    }

    /**
     * 根据商品ID获取商品信息
     * @param shopId 商品ID
     * @return 商品信息
     * @throws Exception
     */
    public ShopDO getShopByShopId(Long  shopId){
        DBHelper2 dBHelper2 =new DBHelper2();
        String sql = "SELECT * FROM shop where id="+shopId;
        return  (ShopDO) dBHelper2.findOne(sql,ShopDO.class);
    }

    public boolean deductionShopById(Long shopId){
        DBHelper2 dBHelper2 =new DBHelper2();
        String sql = "UPDATE shop SET number = number-1 WHERE id ="+shopId;
        return  dBHelper2.executeUpdate(sql);
    }
}

5、更新售卖实现类

package com.restructure.demo.demo2;

import com.restructure.demo.OutInfo;
import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.dao.ShopMapper;

/**
 * 自动售卖机
 */
public class VendingMachine2{

    public static void main(String[] args){
        String userName="哈比";
        long   price = 10L;
        int    port = 2;
        vendingMachine(userName,price,port);
    }
    /**
     * 得到商品
     * @param price    钱
     * @param port     出货口
     * @return
     */
    public static void vendingMachine(String userName,long price, Integer port){
        //获取商品
        ShopMapper shopMapper=new ShopMapper();
        ShopDO shopDO=shopMapper.getShopByPort(port);
        //判断价格 & 库存
        if(shopDO ==null ||  shopDO.getId() == null || price < shopDO.getShopPrice() || shopDO.getNumber() <=0){
            System.out.println("该出货口暂无商品可销售");
            return;
        }
        //扣减库存
        Boolean bool=shopMapper.deductionShopById(shopDO.getId());
        if(bool){
            OutInfo.log(userName,price,port,shopDO);
        }
    }
}

6、增加输出类

package com.restructure.demo;

import com.restructure.demo.demo2.dao.ShopMapper;

public class OutInfo {

    /**
     * 输出日志
     * @param userName
     * @param price
     * @param port
     * @param shopDO
     */
    public static void log(String userName,long price, Integer port,ShopDO shopDO){
        ShopMapper shopMapper=new ShopMapper();
        ShopDO nShopDO=shopMapper.getShopByShopId(shopDO.getId());
        //信息输出
        long giveChance=price-nShopDO.getShopPrice();
        String yNumbers=shopDO.getNumber().toString();
        String nNumbers=nShopDO.getNumber().toString();
        outInfo(userName,price,port.toString(),shopDO.getShopName(),shopDO.getShopPrice().toString(),giveChance,yNumbers,nNumbers);
    }

    /**
     * 信息输出
     * @param userName      用户名
     * @param price         价格
     * @param ports         出货口
     * @param shopNames     商品名
     * @param shopPrices    商品价格
     * @param giveChance    找零
     * @param yNumbers      原库存
     * @param nNumbers      现在库存
     */
    public static void outInfo(String userName,Long price,String ports,String shopNames,String shopPrices,long giveChance,String yNumbers,String nNumbers){
        System.out.println("==================================================");
        System.out.println("购买用户:"+userName);
        System.out.println("投入金额:"+price+"元");
        System.out.println("出 货 口:"+ports);
        System.out.println("购买商品:"+shopNames);
        System.out.println("商品价格:"+shopPrices+"元");
        System.out.println("找    零:"+giveChance+"元");
        System.out.println("原 库 存:"+yNumbers);
        System.out.println("剩余库存:"+nNumbers);
        System.out.println("==================================================");
    }
}

7、疑问?

  • 问:现在代码看起来怎么样?

  • 答:这个代码看起来比刚开始的清爽多了,而且没有多余的代码,也加了注释,感觉舒服多了。为什么要这样优化呢?答案就在上面(6、如何来实施代码重构)我描述的那些。那么问题来了(下面)?

  • 问:那这样就是最优化的代码了么?

  • 答:肯定不是呀,我们还没扩展呢,如果要扩展的话(自动售卖机支持多个商品一起购买)有人会说,这不简单我只要多加一个实现类或者改下现在的入参然后循环不就好了么;但是想想,这样的扩展是我所需要的么?这样不灵活;而且可能这个需求加了之后,我后面有不需要了,只需要单个的商品购买,所以,怎么办呢?这个时候我们就可以考虑设计模式了?

  • 问:为啥一开始没有想到这个?

  • 答:代码的优化是随着需求一步步来的,有些需求你可能没考虑到,但是要上到生产环境的时间到了,很急很急那种,来不及修改了,只能下个版本迭代了;

  • 问:就算用设计模式,我该怎么选择?

  • 答:那么多设计模式,怎么选着使用哪一个是一个很专业的问题,如果用了错误的设计模式可能会导致,代码越来越复杂,耦合更高,代码质量变差,变得不容易维护,选择设计模式要有以下几点注意:

  1. 你必须得会你选择的设计模式;
  2. 理解它(能从生活的日常去理解它最好,知道它是什么?为什么出现?怎么去实现?与其它模式的区别?);
  3. 使用设计模式的目的要明确;
  4. 不影响原来的业务;

(推荐:Java 设计模式)

代码优化(二)-- 设计模式错误使用事例

1、实体类、底层数据、连接数据库驱动、公共类,不需要改动
2、扩展需求,我们选择责任链模式来更改代码
3、新增 SaleShop 类

package com.restructure.demo.demo3;

/**
 * 售卖商品
 */
public abstract class SaleShop {

    private SaleShop saleShop;

    public SaleShop getSaleShop() {
        return saleShop;
    }

    public void setSaleShop(SaleShop saleShop) {
        this.saleShop = saleShop;
    }

    abstract void execute(String userName, long price, Integer port);
}

4、新增 SaleShopListPort 类——多个出货口一起购买实现

package com.restructure.demo.demo3;

import com.restructure.demo.OutInfo;
import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.dao.ShopMapper;

import java.util.ArrayList;
import java.util.List;

/**
 * 多个端口实现类
 */
public class SaleShopListPort extends SaleShop{

    private List<Integer> portList;

    public SaleShopListPort(List<Integer> portList){
        this.portList=portList;
    }

    @Override
    void execute(String userName, long price, Integer port) {
        if(portList==null || portList.size() <= 0){
            getSaleShop().execute(userName,price,port);
        }else{
            ShopMapper shopMapper=new ShopMapper();
            String shopName="",yNumbers="",nNumbers="",portListStr="";
            Long totalPrice=0L;
            List<ShopDO> shopDOLis=new ArrayList<ShopDO>();
            for (int lport:portList) {
                //获取商品
                ShopDO shopDO=shopMapper.getShopByPort(lport);
                //判断价格 & 库存
                if(shopDO == null || shopDO.getId() == null || shopDO.getNumber() <=0){
                    System.out.println("【"+lport+"】出货口暂无商品可销售");
                    return;
                }
                shopName = shopDO.getShopName()+","+shopName;
                totalPrice = totalPrice+shopDO.getShopPrice();
                shopDOLis.add(shopDO);
                portListStr = lport+","+portListStr;
            }
            //判断总金额
            if(price < totalPrice){
                System.out.println("金额错误");
                return;
            }
            //执行扣除库存
            for (ShopDO shopDO:shopDOLis) {
                //扣减库存
                Boolean bool=shopMapper.deductionShopById(shopDO.getId());
                if(bool){
                    ShopDO nShopDO=shopMapper.getShopByShopId(shopDO.getId());
                    yNumbers = shopDO.getNumber()+","+yNumbers;
                    nNumbers = nShopDO.getNumber()+","+nNumbers;
                }
            }
            Long giveChance=price-totalPrice;
            OutInfo.outInfo(userName,price,portListStr,shopName,totalPrice.toString(),giveChance,yNumbers,nNumbers);
        }
    }

}

5、新增 SaleShopOnePort 类——单个出货口一起购买实现

package com.restructure.demo.demo3;

import com.restructure.demo.OutInfo;
import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.dao.ShopMapper;

import java.util.List;

/**
 * 多个端口实现类
 */
public class SaleShopOnePort extends SaleShop{

    private List<Integer> portList;

    public SaleShopOnePort(List<Integer> portList){
        this.portList=portList;
    }

    @Override
    void execute(String userName, long price, Integer port) {
        if(portList==null || portList.size() <= 0){
            ShopMapper shopMapper=new ShopMapper();
            //获取商品
            ShopDO shopDO=shopMapper.getShopByPort(port);
            //判断价格 & 库存
            if(shopDO ==null || price < shopDO.getShopPrice() || shopDO.getNumber() <=0){
                System.out.println("该出货口暂无商品可销售");
                return;
            }
            //扣减库存
            Boolean bool=shopMapper.deductionShopById(shopDO.getId());
            if(bool){
                ShopDO nShopDO=shopMapper.getShopByShopId(shopDO.getId());
                //信息输出
                long giveChance=price-nShopDO.getShopPrice();
                String yNumbers=shopDO.getNumber().toString();
                String nNumbers=nShopDO.getNumber().toString();
                OutInfo.outInfo(userName,price,port.toString(),shopDO.getShopName(),shopDO.getShopPrice().toString(),giveChance,yNumbers,nNumbers);
            }
        }else{
            getSaleShop().execute(userName,price,port);
        }
    }
}

6、测试类

package com.restructure.demo.demo3;

import java.util.ArrayList;
import java.util.List;


/**
 * 自动售卖机
 */
public class VendingMachine3 {

    public static void main(String[] args) {
        //通过责任链模式实习在不改动接口参数的情况下,对多个端口传入参数的实现
        List<Integer> portList=new ArrayList<Integer>();
        portList.add(1);
        portList.add(2);

        SaleShop saleShopListPort=new SaleShopListPort(portList);
        SaleShop saleShopOnePort=new SaleShopOnePort(portList);

        saleShopListPort.setSaleShop(saleShopOnePort);
        String userName="哈比";
        long   price = 10L;
        int    port = 2;
        saleShopListPort.execute(userName,price,port);
    }
}

**总结:**上面的例子虽然也能运行,但是该设计模式不适合此场景,我们希望灵活的扩展和组装;目前的扩展使得代码更加繁琐了,耦合性贼高的,与我们的初衷违背了。

代码优化(三)-- 切换使用的设计模式

1、使用策略模式重构代码实体类、底层数据、连接数据库驱动、公共类,不需要改动
2、创建策略接口

package com.restructure.demo.demo4;

import java.util.List;

public interface Strategy {

    /**
     * 策略方法--自动售卖机
     * @param userName  用户
     * @param price     价格
     * @param portList  出货口
     */
    public void vendingMachine(String userName,long price, List<Integer> portList);
}

3、创建策略体 VendingMachine4

package com.restructure.demo.demo4;

import java.util.List;

/**
 *
 */
public class VendingMachine4 {

    //持有一个具体策略的对象
    private Strategy strategy;

    /**
     * 构造函数,传入一个具体策略对象
     * @param strategy    具体策略对象
     */
    public VendingMachine4(Strategy strategy){
        this.strategy = strategy;
    }

    /**
     * 策略方法
     */
    public void vendingMachine(String userName,long price, List<Integer> portList){
        strategy.vendingMachine(userName,price,portList);
    }
}

4、创建单个售卖实现类

package com.restructure.demo.demo4;

import com.restructure.demo.OutInfo;
import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.dao.ShopMapper;

import java.util.List;

/**
 * 单个出货口实现类
 */
public class StrategyOneImpl implements Strategy {

    @Override
    public void vendingMachine(String userName, long price, List<Integer> portList) {
       ;
        if(portList!=null && portList.size() == 1){
            int port = portList.get(0).intValue();
            ShopMapper shopMapper=new ShopMapper();
            //获取商品
            ShopDO shopDO=shopMapper.getShopByPort(port);
            //判断价格 & 库存
            if(shopDO ==null || price < shopDO.getShopPrice() || shopDO.getNumber() <=0){
                System.out.println("该出货口暂无商品可销售");
                return;
            }
            //扣减库存
            Boolean bool=shopMapper.deductionShopById(shopDO.getId());
            if(bool){
                OutInfo.log(userName,price,port,shopDO);
            }
        }
    }
}

5、创建多个出货口实现

package com.restructure.demo.demo4;

import com.restructure.demo.OutInfo;
import com.restructure.demo.ShopDO;
import com.restructure.demo.demo2.dao.ShopMapper;

import java.util.ArrayList;
import java.util.List;

/**
 * 多出货口实现类
 */
public class StrategyListImpl implements Strategy {

    @Override
    public void vendingMachine(String userName, long price, List<Integer> portList) {
        ShopMapper shopMapper=new ShopMapper();
        String shopName="",yNumbers="",nNumbers="",portListStr="";
        Long totalPrice=0L;
        List<ShopDO> shopDOLis=new ArrayList<ShopDO>();
        for (int port:portList) {
            //获取商品
            ShopDO shopDO=shopMapper.getShopByPort(port);
            //判断价格 & 库存
            if(shopDO == null || shopDO.getId() == null || shopDO.getNumber() <=0){
                System.out.println("【"+port+"】出货口暂无商品可销售");
                return;
            }
            shopName = shopDO.getShopName()+","+shopName;
            totalPrice = totalPrice+shopDO.getShopPrice();
            shopDOLis.add(shopDO);
            portListStr = port+","+portListStr;
        }

        //判断总金额
        if(price < totalPrice){
            System.out.println("金额错误");
            return;
        }
        //执行扣除库存
        for (ShopDO shopDO:shopDOLis) {
            //扣减库存
            Boolean bool=shopMapper.deductionShopById(shopDO.getId());
            if(bool){
                ShopDO nShopDO=shopMapper.getShopByShopId(shopDO.getId());
                yNumbers = shopDO.getNumber()+","+yNumbers;
                nNumbers = nShopDO.getNumber()+","+nNumbers;
            }
        }
        Long giveChance=price-totalPrice;
        OutInfo.outInfo(userName,price,portListStr,shopName,totalPrice.toString(),giveChance,yNumbers,nNumbers);
    }
}

6、创建测试demo

package com.restructure.demo.demo4;

import java.util.ArrayList;
import java.util.List;

public class Demo {

    public static void main(String[] args) {
        String userName="哈比";
        long   price = 10L;
        List<Integer> portList=new ArrayList<Integer>();
        portList.add(1);
        portList.add(2);

        //这里可以采用抽象工厂模式哦!
        Strategy strategy=null;
        //选择并创建需要使用的策略对象
        if(portList!=null && portList.size() ==1){
            strategy = new StrategyOneImpl();
        }else{
            strategy = new StrategyListImpl();
        }
        VendingMachine4 vendingMachine4=new VendingMachine4(strategy);
        vendingMachine4.vendingMachine(userName,price,portList);
    }
}

根据上述代码,我们可以清晰的看到,这次的优化比上一次用的设计模式用的更清晰,更容易扩展,是不是美滋滋,当然了,其中有些地方还可以优化,预留在这里,大家可以把代码下下来,然后去优化下,我的讲解就到这里了,希望大家喜欢,其实重重构不是很难很难,最主要的是实际场景特别复杂,牵一发而动全身,所以小编建议大家在不了解整个的业务的情况下,不要轻易的动工;如果你要选择使用设计模式,那么你千万要选对。

可以继续优化的点:
  1. 数据库链接这里可以使用模板模式;
  2. 如果我们需要重构代码不影响原来的实现,那么我们可以使用适配器模式;

我的文章偏向于代码实践讲解,希望大家多多提些意见,我好改正!另外,对于设计模式的讲解,我博客有,大家可以看看! 讲诉代码可能很枯燥,所以我添加了一点日常的话语,希望大家多多包涵。

下载地址:百度网盘,提取码:2xgs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值