好的代码,应该如何去定义?
我记得美国有过这样一个调查,从2014年到2015年一年时间里,他们询问了65名经验在5年以上的开发人员后,最终得出了这样一个结论:“好的代码是可读的,可理解的,覆盖了自动化测试的,不过于复杂,并且能办好我们需要它做的事情。”
听起来很棒,Right?
然而,应该如何把“他们”引入我们的项目中呢?而这一切的一切,我认为还要从,接口和抽象类说起。
接口和抽象类有什么区别?为什么接口和抽象类不能实例化?为什么接口和抽象类必须由子类实现?
因为计算机语言其实是人类思想的延伸,在机器的世界里,只需要子类就能完成具体逻辑,而接口和抽象类才是整个思维脉络的具体展现:接口是完成所需功能的具体执行步骤即顺序,而抽象类不仅是顺序还是相同类型的共享集合。
我们以一个需求来举例:有一家工厂,因为市场需求,他们分别生产不同的小汽车:大、中、小。
好了,让我们来编织思维,首先,抽象核心:产品和工厂,这里的抽象产品当然就是小汽车,子产品,就是:大中小三种汽车,抽象工厂当然也就是生产厂家,对应的也是大中小三汽车生产线。思维框架搭起来,我们就开始搭建软件框架。
Test.Project
首先,搭起框架,不必急于编码,就算你明知有些子类可能为空,或者没有几行或者完全可以和别的类合并也一样。
然后,开始编写内容。
ICar.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.interfaces;
/**
* 汽车定制抽象产品接口
*
* @auther kay
* @since 1.0
*/
public interface ICar {
void getName();
}
LowCar.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entity;
import com.kay.cn.interfaces.ICar;
/**
* 低端汽车具体产品类
*
* @auther kay
* @since 1.0
*/
public class LowCar implements ICar{
public void getName() {
System.out.println("LowCar");
}
}
MidCar.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entity;
import com.kay.cn.interfaces.ICar;
/**
* 中端汽车具体产品类
*
* @auther kay
* @since 1.0
*/
public class MidCar implements ICar{
public void getName() {
System.out.println("MidTop");
}
}
TopCar.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entity;
import com.kay.cn.interfaces.ICar;
/**
* 高端汽车具体产品类
*
* @auther kay
* @since 1.0
*/
public class TopCar implements ICar{
public void getName() {
System.out.println("TopCar");
}
}
AbstractFactory.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.factory;
import com.kay.cn.interfaces.ICar;
/**
* 抽象工厂
*
* @auther kay
* @since 1.0
*/
public abstract class AbstractFactory {
public abstract ICar create();
}
LowFactory.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entityfactory;
import com.kay.cn.entity.LowCar;
import com.kay.cn.factory.AbstractFactory;
import com.kay.cn.interfaces.ICar;
/**
* 低档工厂
*
* @auther kay
* @since 1.0
*/
public class LowFactory extends AbstractFactory {
public ICar create() {
return new LowCar(); //低档工厂生成低档小汽车对象
}
}
MidFactory.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entityfactory;
import com.kay.cn.entity.MidCar;
import com.kay.cn.factory.AbstractFactory;
import com.kay.cn.interfaces.ICar;
/**
* 中档工厂
*
* @auther kay
* @since 1.0
*/
public class MidFactory extends AbstractFactory {
public ICar create() {
return new MidCar(); //中档工厂生成中档小汽车对象
}
}
TopFactory.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.entityfactory;
import com.kay.cn.entity.TopCar;
import com.kay.cn.factory.AbstractFactory;
import com.kay.cn.interfaces.ICar;
/**
* 高档工厂
*
* @auther kay
* @since 1.0
*/
public class TopFactory extends AbstractFactory {
public ICar create() {
return new TopCar(); //高档工厂生成高档小汽车对象
}
}
Run.java
/**
* Copyright 2015 the original kay(1132892414@qq.com)
*/
package com.kay.cn.run;
import com.kay.cn.entityfactory.TopFactory;
import com.kay.cn.factory.AbstractFactory;
import com.kay.cn.interfaces.ICar;
/**
* 测试类
*
* @auther kay
* @since 1.0
*/
public class Run {
public static void main(String[] args) {
AbstractFactory obj = new TopFactory();
ICar car = obj.create();
car.getName();
}
}
这段代码最简单的语义描述就是“我们是小汽车工厂,生产并管理小汽车”。也就是说,抽象产品+抽象工厂定义好了,需要完成的工厂基本也就清晰了,就能马上转化为具体的代码实现。
其实,面向对象真正的魅力也正在于此。代码即思维,而思维源于生活。所以,我们编写的并不是软件,而是人们有了软件后,所能做的事,因为我相信无论是电脑还是软件,或是其他的什么,都是人类的一种延伸。人性是贪婪的,人们总是希望我们能从中获得更多,无论是对于工作、婚姻、金钱还是生活。这就是我们要做的,就是对于无限可能的信仰,化不可能为可能,你要让人们相信:无论你有怎样的梦想,你都可以通过他实现。
所以,你所编写的东西,其真正的目的是为了打动别人、激发别人。所以谁会喜欢和一个丑陋的东西打交道呢?我所说的丑陋,不仅仅指UI而已,我们编写她应该不仅仅因为她能够做到我们所需要她做到的事,而且更应该充满艺术感,但没人在乎,这个世界错误的认为她应该非黑即白,但我们的生活,绘画和梦境都是彩色的,所以我认为她也应该是这样。
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* Central interface to provide configuration for an application.
* This is read-only while the application is running, but may be
* reloaded if the implementation supports this.
*
* <p>An ApplicationContext provides:
* <ul>
* <li>Bean factory methods for accessing application components.
* Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
* <li>The ability to load file resources in a generic fashion.
* Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
* <li>The ability to publish events to registered listeners.
* Inherited from the {@link ApplicationEventPublisher} interface.
* <li>The ability to resolve messages, supporting internationalization.
* Inherited from the {@link MessageSource} interface.
* <li>Inheritance from a parent context. Definitions in a descendant context
* will always take priority. This means, for example, that a single parent
* context can be used by an entire web application, while each servlet has
* its own child context that is independent of that of any other servlet.
* </ul>
*
* <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
* lifecycle capabilities, ApplicationContext implementations detect and invoke
* {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see ConfigurableApplicationContext
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.core.io.ResourceLoader
*/
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
/**
* Return the unique id of this application context.
* @return the unique id of the context, or {@code null} if none
*/
String getId();
/**
* Return a name for the deployed application that this context belongs to.
* @return a name for the deployed application, or the empty String by default
*/
String getApplicationName();
/**
* Return a friendly name for this context.
* @return a display name for this context (never {@code null})
*/
String getDisplayName();
/**
* Return the timestamp when this context was first loaded.
* @return the timestamp (ms) when this context was first loaded
*/
long getStartupDate();
/**
* Return the parent context, or {@code null} if there is no parent
* and this is the root of the context hierarchy.
* @return the parent context, or {@code null} if there is no parent
*/
ApplicationContext getParent();
/**
* Expose AutowireCapableBeanFactory functionality for this context.
* <p>This is not typically used by application code, except for the purpose of
* initializing bean instances that live outside of the application context,
* applying the Spring bean lifecycle (fully or partly) to them.
* <p>Alternatively, the internal BeanFactory exposed by the
* {@link ConfigurableApplicationContext} interface offers access to the
* {@link AutowireCapableBeanFactory} interface too. The present method mainly
* serves as a convenient, specific facility on the ApplicationContext interface.
* <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
* after the application context has been closed.</b> In current Spring Framework
* versions, only refreshable application contexts behave that way; as of 4.2,
* all application context implementations will be required to comply.
* @return the AutowireCapableBeanFactory for this context
* @throws IllegalStateException if the context does not support the
* {@link AutowireCapableBeanFactory} interface, or does not hold an
* autowire-capable bean factory yet (e.g. if {@code refresh()} has
* never been called), or if the context has been closed already
* @see ConfigurableApplicationContext#refresh()
* @see ConfigurableApplicationContext#getBeanFactory()
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
这是Spring源代码中随便找的一段代码,我想任何一个刚刚学会编程而且懂英文的人,都能大致的说出她的作用,而且就算给很资深的程序员也几乎没办法再继续做结构、功能、效率方面的简化了。这就是我们想要的效果。
赏心悦目的代码,不仅仅可以很好体现易读性和易维护性,更会让人有兴趣去了解她。其实,这对一个成天和代码打交道的人来说是至关重要。所以,作为程序员的我们,应该尽我们一切的可能,去做到几个简单却至关重要的原则,以编写出好的代码:
1)简洁
我们的目的是编写出优雅简洁的代码。程序结构一定要清晰,并且简单易懂,单个函数的程序行数不应该超过100行。我个人认为做好的行数是30-50行,刚好一个屏幕,宽度不要超过100,过长的代码量不仅会影响大家理解,也会消耗架构的敏捷性。还要明确一个函数的目的性,所以其职责一定要单一。在实现时,一定要直接了当,代码精简,避免产生任何的垃圾代码。另外,尽量使用标准函数库和公共函数库。重复造轮子不仅浪费时间而且还不一定稳定。模块化的去编程,不仅是后台,还有前端。
2)规范
很多程序员不屑于编码规则,他们时常在项目中的变量或者常量简单的命名为aa、bb、cc。仅在这几行好像没什么大问题,但过个一年半载,天知道这是什么意思。还有不喜欢写注释,这是一个更不好的习惯。我们开发过程中,一定要非常严格的遵守编码规范。例如:1 .每个源程序文件都有头文件说明。2. 每个函数都有函数头说明。3. 定义或引用主要变量(结构、联合、类或对象)时,用注释反映含义。4. 常量定义要有相应的说明。5. 处理每个阶段都应该有相关的注释说明。6. 在典型算法前都有注释。等等。这对于经常要看代码的人来说,应该是深有感触的。
3)高效
提升性能的基本方法,1,不要再循环条件中计算。2,尽可能用final static。3,尽量缩小变量使用范围。4,学会使用StringBuilder等取代String。5,复写异常中fillInStackTrack方法。等等方法非常多。一般而言,没有慢的系统,只有架构不良的系统。使用好的架构,就不会有太多效率方面的问题,即使出现了也一般是二八原则,卡在了哪一个点,这时候需要程序员,深入思考,寻根探源。“病来如山倒,病去如抽丝”,系统优化也是相同的过程,一定不可急躁。
4 ) 健壮
一个比较模糊的概念,但却是非常重要的软件外部量度标准。简单来说,我们知道正确性是软件在需求之内的执行情况,而健壮性则是需求之外的执行情况。可以多使用异常,把性能问题放一边,而且我们提倡异常封装,因为一般Java API提供的异常都是比较低级的,只有开发人员才能看得懂具体发生了什么,而对于普通人来说,这简直是天书,合理的异常封装可以提高系统的友好度以及可维护性,也填补了Java异常处理机制的自身缺陷。但不建议finally里处理返回值以及在构造函数里抛出异常。
5 ) 可扩展
遵循经典的“开放-封闭”原则,而且心目中永远要有一个远大的目标,时刻做好准备。
6)可靠
避免发生故障的能力。还记得那个做Android刚满一个月就被老板踢断腿的人吗?
7 ) 适应
当今计算机环境复杂,浏览器产品也类型繁多。虽然这是外部原因,但我们必须要求他们在所有地方都能运行。
8 ) 开源
大胆的采用开源工具,MVC框架有struts,也有Spring MVC、WebWorker;Ioc 容器有Spring,也有Google Guice;ORM 既有Hibernate,也有MyBatis;日志记录有经典的log4j,也有崭新的logback。可选的很多,以至于无从选择。“大树底下好乘凉”在大树下,我们才有时间和精力纳凉,而不会把大量时间用于排查Bug上。而且同时,因为热度越高,Bug曝光率也越快,修复率也就越高,这对我们项目的稳定性来说非常重要。但很多开源项目可能已经很长没有更新或者濒临关闭了,我们不能要求太高,毕竟开源项目已经共享了他人的精力和智力,我们应该珍惜他人的劳动成果,最低标准,不要去诋毁。
还有永远不要说,“如果时间允许我一定会写出更好的代码”,诸如此类的话。
言外之意,就是如果时间不足我们就有放宽对代码质量的要求的权利了?
我们应该在任何时刻都要保持精益求精,务求代码的正确无误,务求代码的清晰可读。一旦嗅到代码的“坏味道”,我们就应该及时重构。对于烂代码,我们就应该尽力去驯服!其次,心里一定要有一个想要去纠正的问题,而且他必须要能唤起你的激情,否则你必然没有毅力去把它完成。
也永远不要说,不可能做的更快更好这样的话。而是要想,将会数千万人会用到你的东西。你自然而然就知道怎么做了。
人生能做的事情没有几件,而现在我们选择了去做这个,那就应该把他做到最好。
当你长大了,总有人对你说,这个世界有他的规则,你的人生也是在,这样的世界上度过的,别老是想着去打破规则。这样的人生太过狭隘,人生可以更加宽广,只要你能领悟一个简单的道理,那就是你身边的一切,所谓的生活,都是些不比你聪明的人,创造出来的。你能改变他,你可以影响他,你能自己创造出对别人有用的东西,一旦你跳出那个,“生活不可改变,只能去适应的荒谬观点”,转而拥抱它,改变它,升华它,给它烙上你的印记,一旦你明白这点,你的人生将从此不同。
——水门(2016年1月写于杭州)