作者:微信小助手
发布时间:2021-07-12T19:44
什么样的代码是好代码呢?好的代码应该命名规范、可读性强、扩展性强、健壮性......而不好的代码又有哪些典型特征呢?这25种代码坏味道大家要注意啦
重复代码就是不同地点,有着相同的程序结构。一般是因为需求迭代比较快,开发小伙伴担心影响已有功能,就复制粘贴造成的。重复代码很难维护的,如果你要修改其中一段的代码逻辑,就需要修改多次,很可能出现遗漏的情况。
如何优化重复代码呢?分三种情况讨论:
class A {
public void method1() {
doSomething1
doSomething2
doSomething3
}
public void method2() {
doSomething1
doSomething2
doSomething4
}
}
优化手段:可以使用Extract Method(提取公共函数) 抽出重复的代码逻辑,组成一个公用的方法。
class A {
public void method1() {
commonMethod();
doSomething3
}
public void method2() {
commonMethod();
doSomething4
}
public void commonMethod(){
doSomething1
doSomething2
}
}
class A extend C {
public void method1() {
doSomething1
doSomething2
doSomething3
}
}
class B extend C {
public void method1() {
doSomething1
doSomething2
doSomething4
}
}
优化手段:对两个类都使用Extract Method(提取公共函数),然后把抽取出来的函数放到父类中。
class C {
public void commonMethod(){
doSomething1
doSomething2
}
}
class A extend C {
public void method1() {
commonMethod();
doSomething3
}
}
class B extend C {
public void method1() {
commonMethod();
doSomething4
}
}
如果是两个毫不相关的类出现重复代码,可以使用Extract Class将重复代码提炼到一个类中。这个新类可以是一个普通类,也可以是一个工具类,看具体业务怎么划分吧。
长函数是指一个函数方法几百行甚至上千行,可读性大大降低,不便于理解。反例如下:
public class Test {
private String name;
private Vector<Order> orders = new Vector<Order>();
public void printOwing() {
//print banner
System.out.println("****************");
System.out.println("*****customer Owes *****");
System.out.println("****************");
//calculate totalAmount
Enumeration env = orders.elements();
double totalAmount = 0.0;
while (env.hasMoreElements()) {
Order order = (Order) env.nextElement();
totalAmount += order.getAmout();
}
//print details
System.out.println("name:" + name);
System.out.println("amount:" + totalAmount);
......
}
}
可以使用Extract Method
,抽取功能单一的代码段,组成命名清晰的小函数,去解决长函数问题,正例如下:
public class Test {
private String name;
private Vector<Order> orders = new Vector<Order>();
public void printOwing() {
//print banner
printBanner();
//calculate totalAmount
double totalAmount = getTotalAmount();
//print details
printDetail(totalAmount);
}
void printBanner(){
System.out.println("****************");
System.out.println("*****customer Owes *****");
System.out.println("****************");
}
double getTotalAmount(){
Enumeration env = orders.elements();
double totalAmount = 0.0;
while (env.hasMoreElements()) {
Order order = (Order) env.nextElement();
totalAmount += order.getAmout();
}
return totalAmount;
}
void printDetail(double totalAmount){
System.out.println("name:" + name);
System.out.println("amount:" + totalAmount);
}
}
一个类做太多事情,维护了太多功能,可读性变差,性能也会下降。举个例子,订单相关的功能你放到一个类A里面,商品库存相关的也放在类A里面,积分相关的还放在类A里面...反例如下:
Class A{
public void printOrder(){
System.out.println("订单");
}
public void printGoods(){
System.out.println("商品");
}
public void printPoints(){
System.out.println("积分");
}
}
试想一下,乱七八糟的代码块都往一个类里面塞,还谈啥可读性。应该按单一职责,使用Extract Class
把代码划分开,正例如下:
Class Order{
public void printOrder(){
System.out.println("订单");
}
}
Class Goods{
public void printGoods(){
System.out.println("商品");
}
}
Class Points{
public void printPoints(){
System.out.println("积分");
}
}
}
方法参数数量过多的话,可读性很差。如果有多个重载方法,参数很多的话,有时候你都不知道调哪个呢。并且,如果参数很多,做新老接口兼容处理也比较麻烦。
public void getUserInfo(String name,String age,String sex,String mobile){
// do something ...
}
如何解决过长参数列问题呢?将参数封装成结构或者类,比如我们将参数封装成一个DTO类,如下:
public void getUserInfo(UserInfoParamDTO userInfoParamDTO){
// do something ...
}
class UserInfoParamDTO{
private String name;
private String age;
private String sex;
private String mobile;
}
对程序进行维护时, 如果添加修改组件, 要同时修改一个类中的多个方法, 那么这就是 Divergent Change。举个汽车的例子,某个汽车厂商生产三种品牌的汽车:BMW、Benz和LaoSiLaiSi,每种品牌又可以选择燃油、纯电和混合动力。反例如下:
/**
* 公众号:捡田螺的小男孩
*/
public class Car {
private String name;
void start(Engine engine) {
if ("HybridEngine".equals(engine.getName())) {
System.out.println("Start Hybrid Engine...");
} else if ("GasolineEngine".equals(engine.getName())) {
System.out.println("Start Gasoline Engine...");
} else if ("ElectricEngine".equals(engine.getName())) {
System.out.println("Start Electric Engine");
}
}
void drive(Engine engine,Car car) {
this.start(engine);
System.out.println("Drive " + getBrand(car) + " car...");
}
String getBrand(Car car) {
if ("Baoma".equals(car.getName())) {
return "BMW";
} else if ("BenChi".equals(car.getName())) {
return "Benz";
} else if ("LaoSiLaiSi".equals(car.getName())) {
return "LaoSiLaiSi";
}
return null;
}
}
如果新增一种品牌新能源电车,然后它的启动引擎是核动力呢,那么就需要修改Car类的start
和getBrand
方法啦,这就是代码坏味道:Divergent Change (发散式变化)。
如何优化呢?一句话总结:拆分类,将总是一起变化的东西放到一块。
★”
运用提炼类(Extract Class) 拆分类的行为。 如果不同的类有相同的行为,提炼超类(Extract Superclass) 和 提炼子类(Extract Subclass)。
正例如下:
因为Engine是独立变化的,所以提取一个Engine接口,如果新加一个启动引擎,多一个实现类即可。如下:
//IEngine
public interface IEngine {
void start();
}
public class HybridEngineImpl implements IEngine {
@Override
public void start() {
System.out.println("Start Hybrid Engine...");
}
}
因为drive
方法依赖于Car,IEngine,getBand
方法;getBand
方法是变化的,也跟Car是有关联的,所以可以搞个抽象Car的类,每个品牌汽车继承于它即可,如下
public abstract class AbstractCar {
protected IEngine engine;
public AbstractCar(IEngine engine) {
this.engine = engine;
}
public abstract void drive();
}
//奔驰汽车
public class BenzCar extends AbstractCar {
public BenzCar(IEngine engine) {
super(engine);
}
@Override
public void drive() {
this.engine.start();
System.out.println("Drive " + getBrand() + " car...");
}
private String getBrand() {
return "Benz";
}
}
//宝马汽车
public class BaoMaCar extends AbstractCar {
public BaoMaCar(IEngine engine) {
super(engine);
}
@Override
public void drive() {
this.engine.start();
System.out.println("Drive " + getBrand() + " car...");
}
private String getBrand() {
return "BMW";
}
}
细心的小伙伴,可以发现不同子类BaoMaCar和BenzCar的