关于Java开发的几点建议

导语:在每一位刚入行的程序员的心中,编写程序都是一门神圣的艺术创作。他们无不希望自己的代码作品既简洁清晰,又可读性强,而且还具有一定的容错能力。写这篇文章的目的并不是嘲讽和我一样做Java的同行们,只是希望读者看到此篇文章后,可以和我一样,心平气和的争取做一个优秀的程序员。

方向:作为一个程序员 我们心中一定要有一种危机感 因为技术是瞬息万变的,终身学习在你准备入行的那一刻应该就要被养成

及时更换IDE:真的是时候更换你的IDE了 一个好的IDE能够让你把更多的时间和精力花在代码的逻辑分析和推理上,不知道有多少”老”程序员还在使用eclipse,这些程序员们要不就是因循守旧,要不就是根本就不知道其他好的开发工具的存在,eclipse吃内存卡顿的现象以及各种偶然莫名异常的出现,都告知我们是时候寻找新的开发工具了 IntelliJ IDEA 真的很香

正文

Ⅰ.为你的API写一个统一的返回对象吧

抽象是面向对象编程的最值得被推崇的思想 好的抽象是避免重复造轮子的利刃 我们现在来思考一个问题 对于每一个客户端API而言 我们都应该会有 状态码(前端用于判断)返回状态描述 返回值 成功标识 请求时间戳等信息 基于以上几点 我们可以抽象出这么一个Vo

@Data
public class Result<T> implements Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * 成功标志
	 */
	private boolean success = true;

	/**
	 * 返回处理消息
	 */
	private String message = "操作成功!";

	/**
	 * 返回代码
	 */
	private Integer code = 0;
	
	/**
	 * 返回数据对象 data
	 */
	private T result;
	/**
	 * 时间戳
	 */
	private long timestamp = System.currentTimeMillis();
}

那么每一个API的返回都可以使用上述Vo 但是新的问题又来了 code的取值它应该不是随意的 而是前后端约定好的 而且应该是不变的常量 那么这个时候我们便可以考虑给code的取值导出常量

public interface CommonConstant {

	/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
    public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
    /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
    public static final Integer SC_OK_200 = 200;
	public static final Integer SC_INTERNAL_SERVER_ERROR_999 = 999;

	public static final Integer SC_INTERNAL_SERVER_ERROR_399 = 399;
}

那么还剩一个问题 我们每次使用这个Vo时都要去new 一个Vo 这是我们无法忍受的基于这一点 我们可以给Result加一些静态方法 于是改造成如下

@Data
public class Result<T> implements Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * 成功标志
	 */
	private boolean success = true;

	/**
	 * 返回处理消息
	 */
	private String message = "操作成功!";

	/**
	 * 返回代码
	 */
	private Integer code = 0;
	
	/**
	 * 返回数据对象 data
	 */
	private T result;

	public Result() {
		
	}
	
	/**
	 * 时间戳
	 */
	private long timestamp = System.currentTimeMillis();

	public void error500(String message) {
		this.message = message;
		this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
		this.success = false;
	}

	public void success(String message) {
		this.message = message;
		this.code = CommonConstant.SC_OK_200;
		this.success = true;
	}
	
	public static Result<Object> error(String msg) {
		return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
	}
	
	public static Result<Object> error(int code, String msg) {
		Result<Object> r = new Result<Object>();
		r.setCode(code);
		r.setMessage(msg);
		r.setSuccess(false);
		return r;
	}
	
	public static Result<Object> okWithMessage(String msg) {
		Result<Object> r = new Result<Object>();
		r.setSuccess(true);
		r.setCode(CommonConstant.SC_OK_200);
		r.setMessage(msg);
		return r;
	}
	

	public static Result<Object> okWithResult(Object data) {
		Result<Object> r = new Result<Object>();
		r.setSuccess(true);
		r.setCode(CommonConstant.SC_OK_200);
		r.setResult(data);
		return r;
	}
	
	public static Result<Object> errorWithResult(Object data) {
		Result<Object> r = new Result<Object>();
		r.setSuccess(false);
		r.setMessage("操作失败!");
		r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
		r.setResult(data);
		return r;
	}
}

于是整个API的返回就被我们优雅的定义出来了

Ⅱ.利用决策+工厂模式 去掉烦人的if-else吧

if-else 本身并没有错 其实复杂的是业务本身 当if-else过于繁多会使代码耦合度越来越高 以至于变得越来越难以维护!基于面向对象的最基本的思想 我们应该对拓展开放而对修改闭合的开闭原则 基于这一思想我们对代码做一些优化,if-else是有办法可以消除掉的,其中比较典型的并且使用广泛的就是借助策略模式和工厂模式,准确的说是利用这两个设计模式的思想,消灭代码中的if-else。

下面我们来看一个例子
假设我们要做一个类似于美团的外卖平台,有这样的需求:
1、平台上的某些店铺为了促销,设置了多种会员优惠,其中包含超级会员折扣8折、普通会员折扣9折和普通用户没有折扣三种。
2、希望用户在付款的时候,根据用户的会员等级,就可以知道用户符合哪种折扣策略,进而进行打折,计算出应付金额。
3、随着业务发展,新的需求要求专属会员要在店铺下单金额大于30元的时候才可以享受优惠。
4、接着,又有一个变态的需求,如果用户的超级会员已经到期了,并且到期时间在一周内,那么就对用户的单笔订单按照超级会员进行折扣,并在收银台进行强提醒,引导用户再次开通会员,而且折扣只进行一次。

基于上述需求 我们写出相应伪代码

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {

    if (用户是专属会员) {
        if (订单金额大于30) {
            returen 7折价格;
        }
    }

    if (用户是超级会员) {
        return 8折价格;
    }

    if (用户是普通会员) {
        if(该用户超级会员刚过期并且尚未使用过临时折扣){
            临时折扣使用次数更新();
            returen 8折价格;
        }
        return 9折价格;
    }
    return 原价;
}

这还只是伪代码 如果写成真实的代码那代码量可想而知有多复杂 而且以后如果又有新的需求 那代码又得改 而且会越到后面if-else 就会越多 新写的代码为了一定可以被执行 而不断的加到最前面 导致整个系统bug越来越多

我们先引入决策模式
回过头来我们想一个问题 我们代码越来越复杂的原因其实很简单 我们把很多代码都耦合在了一起 我们需要对所有的会员定义一种决策方法 那么来一种会员我们就定制化实现一种决策 我们定义一个接口

public interface UserPayService {

    /**
     * 计算应付价格
     */
    public BigDecimal quote(BigDecimal orderPrice);
}

接着定义几个策略类:

//专属会员的决策实现
public class ParticularlyVipPayService implements UserPayService {

@Override
public BigDecimal quote(BigDecimal orderPrice) {
     if (消费金额大于30元) {
        return 7折价格;
    }
}

}
//超级会员的决策实现
public class SuperVipPayService implements UserPayService {

@Override
public BigDecimal quote(BigDecimal orderPrice) {
    return 8折价格;
}

}

//普通会员的决策实现
public class VipPayService implements UserPayService {

@Override
public BigDecimal quote(BigDecimal orderPrice) {
    if(该用户超级会员刚过期并且尚未使用过临时折扣){
        临时折扣使用次数更新();
        returen 8折价格;
    }
    return 9折价格;
}

}

那么现在的问题就很明朗了 以后有新的会员接入 我们只需要拓展实现相应的实现类 它们各自的专属代码都有个性化实现 而不再相互耦合

//基于以上我们的代码变成了这样子的

@Controller
public Result calPrice( BigDecimal  orderPrice,User user) {

     String vipType = user.getVipType();
    

     if (vipType == 专属会员) {
        //伪代码:从Spring中获取超级会员的策略对象
        UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);
        return strategy.quote(orderPrice);
     }

     if (vipType == 超级会员) {
        UserPayService strategy = Spring.getBean(SuperVipPayService.class);
        return strategy.quote(orderPrice);
     }

     if (vipType == 普通会员) {
        UserPayService strategy = Spring.getBean(VipPayService.class);
        return strategy.quote(orderPrice);
     }
     return 原价;
}

我们发现我们现在引入工厂模式只是减少了内部的if-else 代码 外围我们依然需要对 vip类型做判断 我们尝试引入工厂模式试试看

我们可以创建这样一个工厂 维护一个Map<String,UserPayService> Map中的Key为vipType value为它的相应决策类

public class UserPayServiceStrategyFactory {

    private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();

    public  static UserPayService getByUserType(String type){
        return services.get(type);
    }
}

那现在还有一个问题没有解决 我们怎么解决map赋值问题 这个时候我们应该想到Spring +反射 (或者使用自定义注解)这里我们举例写一个SpringBean专门用来扫描相应的包下面的service 将相应的service放入map中 我们改造工厂

@Component
@Data
@Slf4j
public class UserPayServiceStrategyFactory {


    //存放userPayService的包路径 
    private static final String SCANNER_PATH = "com.xxx.xxx";

    /**
     * 模块-监听者映射
     */
     private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>();

    public UserPayServiceStrategyFactory () {
        //初始化
        init();
    }

    private void init() {
        log.info("===业务初始化开始===");
        process();
        log.info("===业务初始化结束===");
    }

    private void process() {
        /**
         * 1.扫描实现UserPayService接口的类
         * 2.将扫描到的类实例化(这一步交给spring处理,使用注解即可)
         * 3.将这些实例添加到Map
         */
        //反射工具包,指明扫描路径
        Reflections reflections = new Reflections(SCANNER_PATH);
        //实现UserPayService接口的类
        Class superClass = UserPayService.class;
        Set<Class> classList = reflections.getSubTypesOf(superClass);

        for (Class<?> aClass : classList) {
            //获取监听器实例
            UserPayService service= (UserPayService) SpringContextUtils.getBean(aClass);
            //加入映射
          UserPayService businessListenerList = services .get("普通会员");
            if (businessListenerList == null) {
                moduleListenersMap.put("普通会员", businessListenerList);
            }
 UserPayService businessListenerList = services .get("专属会员");
  if (businessListenerList == null) {
                moduleListenersMap.put("专属会员", businessListenerList);


 UserPayService businessListenerList = services .get("超级会员");
  if (businessListenerList == null) {
                moduleListenersMap.put("超级会员", businessListenerList);
        }
    }
}

以上我们解决了一个map赋值问题 真实的场景下我们应该使用枚举去描述专属会员 普通会员 超级会员 消除上述代码中的if分支结构 这边不再进行赘述 整体思想就是这样

接下来我们的代码会变得如何优雅 请看

@Controller
public Result calPrice(BigDecimal orderPrice,User user) {

     String vipType = user.getVipType();
     UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType);
     return strategy.quote(orderPrice);
}

所有的if-else 被我们成功干掉 代码的可拓展性和简洁性不言而喻

Ⅲ.拥抱lombok吧

是时候向setter/getter、ashcode/equals、以及constructors/toString等样板式代码 (boilerplate code) 说再见了,您只需要一个注解: @Data 就能统统搞定了。lombok功能特别强大 这边由于篇幅问题就不再赘述 可以上它的官网查看相应功能

Lombok 是一款可以通过简单的注解形式,来帮助开发者简化并消除 Java 代码臃肿的工具 (具体请参见)。它不但能够减少您的代码编写量,还能够帮助您打理那些生成的字节码。 我相信你用完lombok会回来感谢我的

总结:以上的很多东西都是本人写代码的一些经验总结和一些其他博主的一些经验总结 希望能够帮助到各位 让我们一起愉快的优雅写代码吧

特别鸣谢:
八点建议助您写出优雅的Java代码
细思极恐-你真的会写java吗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值