前言
2017年过了大半个月,才总结了第一篇博客,也真是有够懒的~~哈哈,解释解释~主要最近的笔记都零零散散的,开发又紧,所以得慢慢总结汇总成笔记和博客。还记得2016年底被公司安排学习web,悲剧的是学习了不到一星期,由于公司项目开发周期安排紧张,又被调回来Android开发,看来我和Android机器人还是缘分依旧。新技术的潮流趋势,多了解些技术和开发语言也是非常重要的,重在学习,所以会在力所能及的情况下继续学习web,最近也抽了点时间看看设计模式相关的书籍,那么其中就包含面向对象五大原则,又刚好前几天看到一网站博文关于面向对象五大原则在Android中的运用,边看边总结,结合书和博文,就有了2017年第一篇博客总结,day day up!!!
简述
SOLID是面向对象五大基本原则的缩写,分别是:
- 单一职责原则
- 开闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖倒置原则
在日常的项目开发中,随着项目的不断迭代,越感觉这些原则对于编程是多么的重要,所以还是有必要抽空学习的
Ⅰ.单一职责原则
Android日常的开发经常会遇到条目布局,随着Android5.0的普及,原先使用到的ListView大多被RecyclerView所替代,而在使用RecyclerView时需要为其条目设置数据或是多类型条目,那么就需要为RecyclerView设置适配器,使用RecyclerView适配器则需要重写三个方法,分别是onCreateViewHolder、onBindViewHolder和getItemCount.
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//找寻布局的控件,生成ViewHolder
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//将数据显示在控件上
}
@Override
public int getItemCount() {
return 0;
}
}
上面三个方法应该都是非常熟悉的,可以在onCreateViewHolder去将布局设置给holder(布局持有者,作用是找到布局上的控件),在onBindViewHolder方法主要操作是将数据显示在控件上,而getItemCount则返回条目数量,这就是三个方法各自的职责,也就是各自遵守的单一职责,随便看下下面简单的操作。
//RvAdapter类
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
rvHolder.mTvNumber.setText(models.get(position).getNumber());
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
}
//ViewHolder类
public class RvViewHolder extends RecyclerView.ViewHolder{
private TextView mTvNumber;
private TextView mTvPelf;
public RvViewHolder(View itemView) {
super(itemView);
initView();
}
private void initView() {
mTvNumber = (TextView)itemView.findViewById(R.id.tv.number);
mTvPelf = (TextView)itemView.findViewById(R.id.tv.pelf);
}
}
//Model类
public class Model{
String number;
String pelf;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getPelf() {
return pelf;
}
public void setPelf(String pelf) {
this.pelf = pelf;
}
}
适配器重写的三个方法各自的职责操作就跟上面的类似了,每个方法遵循着各自的职责,那么假如现在产品mm来了个新需求,需要对上面的model数据的金额进行公式计算,计算完成之后才能设置在控件上,那么按照日常的开发Code,可爱的工程师们或许会这么写;
//RvAdapter类
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
//对金额进行计算
String result = String.valueOf(Integer.valueOf(models.get(position).getNumber())*0.8)
+(Integer.valueOf(models.get(position).getNumber()) * 0.75 + 100);
rvHolder.mTvNumber.setText(result);
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
有木有觉得上面在onBindViewHolder方法中的操作就违反了单一职责原则,在onBindViewHolder里先做金额数据的运算操作,数据计算完成之后再设置在控件,数据的操作与控件的显示数据这两个操作,该方法中做的就不再是单一的任务,当某天产品又来了新需求,需要增加一个显示收益的数据,还得根据金额计算出收益,那么关于收益的代码是不是类似上面一样在onBindViewHolder方法里进行书写,随着需求越来越多,那么代码就堆成一团咯,可能你也揪心,后面维护的小伙伴看着都揪心…话说回来,又该怎么优化上面关于金额数据的运算及显示呢?看下面代码,让你豁然开朗
//RvAdapter类
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
rvHolder.mTvNumber.setText(models.get(position).getNumber());
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
//Model类
public class Model{
String number;
String pelf;
public String getNumber() {
String result = String.valueOf(Integer.valueOf(number)*0.8)
+(Integer.valueOf(number) * 0.75 + 100);
return result;
}
public void setNumber(String number) {
this.number = number;
}
public String getPelf() {
return pelf;
}
public void setPelf(String pelf) {
this.pelf = pelf;
}
}
上面的适配器里关于数据运算的操作移到Model进行计算了,当执行getNumber方法时,再进行数据的运算操作,刚才假设之后新增需求计算收益数据,那么同样的,在model进行计算并提供get方法,是不是挺简单明了的.在onCreateViewHolder去将布局设置给holder,在onBindViewHolder方法主要操作是将数据显示在控件上,getItemCount则返回条目数量,各自的方法,各自的职责,保持互不干扰.
总结:在开发中,应该使得方法和类保持单一职责原则,这样便于维护而且逻辑清晰
Ⅱ.开闭原则
简述:开闭原则的核心思想是避免修改程序,让程序更加具有扩展性;往往会觉得这不是很矛盾?当你了解开闭原则后,其实并并不会矛盾;日常开发中,java的继承与实现是很常见的,都是体现开闭原则的一大亮点,可能开发中遇到而不知道这代码就是遵循了开闭原则.
那么从例子说起吧,毕竟有例子会比较易懂.假设有这样的需求:计算任何形状的多边形的面积总和,可能由于业务刚刚开始,前期的多边形都是长方形,那么亲爱的开发工程师,该怎么写呢?
//长方形
class Rectangle{
private double width;
private double height;
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
//所有多边形的面积管理类
class AreaManager{
public double computeArea(List<Rectangle> shapes){//负责计算多边形面积总和
double areas = 0;
for(Rectangle rectangle:shapes){
areas += (rectangle.getWidth() * rectangle.getHeight());
}
return areas;
}
}
上面的代码解决了所有多边形为长方形情况下,它们的面积总和的需求。现在假设随着业务逐步扩展,出现了圆形这一角色,很明显上面的代码已然不再适用,那么理所当然的对上面的代码进行更改吧
//圆形
class Circle{
private double radiu;
public double getRadiu() {
return radiu;
}
public void setRadiu(double radiu) {
this.radiu = radiu;
}
}
//所有多边形的面积和管理类
class AreaManager{
public double computeArea(List<Object> shapes){//负责计算多边形面积总和
double areas = 0;
for(Object shape:shapes){
if(shape instanceof Rectangle){
Rectangle rectangle = (Rectangle)shape;
areas += (rectangle.getWidth() * rectangle.getHeight());
}else if(shape instanceof Circle){
Circle cirlce = (Circle)shape;
areas += (cirlce.getRadiu() * cirlce.getRadiu() * Math.PI);
}else{
throw new RuntimeException("Shape not supported");
}
}
return areas;
}
}
如果每次新的多边形角色出现,都得对上面的代码进行if判断和修改,是不是很烦而且代码也不好维护,久而久之,代码可能就乱成一团了,而上面每次的修改也违反了开闭原则,扩展新功能是基于旧代码修改的前提下进行的,这样肯定是不行的,那么又该怎么去避免这种修改呢?由于java面向对象的特性,所以避免这种修改也是挺方便避免的,看代码.
interface Shape{
//用于计算多边形的面积
double getArea();
}
//长方形
class Rectangle implements Shape{
private double width;
private double height;
@Override
public double getArea() {
return width * height;
}
}
//圆形
class Circle implements Shape{
private double radiu;
@Override
public double getArea() {
return radiu * radiu * Math.PI;
}
}
//所有多边形的面积总和计算管理类
class AreaManager{
public double computeArea(List<Shape> shapes){
double areas = 0;
for(Shape shape:shapes){
areas += shape.getArea();
}
return areas;
}
}
//主函数
public static void main(String args){
List<Shape> shapes = new ArrayList<>();
shapes.add(new Rectangle(10, 10));
shapes.add(new Rectangle(6, 6));
shapes.add(new Circle(6));
AreaManager.computeArea(shapes);
}
假设现在的业务中又出现了正方形这角色,上面的代码AreaManager类的代码还需要进行修改吗?显然是不需要的,因为计算面积的方法已经抽象到Shape接口里,任何多边形只要实现Shape接口,重写getArea方法coding关于计算面积的逻辑代码,这样就解决了不同多边形的面积计算的问题。再看看AreaManager类,只有computeArea这个方法,参数是装Shape接口实现类的List集合,而各种多边形的面积由于计算公式并不同,显然不能在computeArea方法里实现,所以多边形自己去实现吧,那么只要在AreaManager类的computeArea方法里去遍历各个多边形,获取其面积getArea()进行累加,这样就简单多了~~
//新增正方形
class Square implements Shape{
private double length;
public Square(double length) {
this.length = length;
}
@Override
public double getArea() {
return length * length;
}
}
//主函数
public static void main(String args){
List<Shape> shapes = new ArrayList<>();
shapes.add(new Rectangle(10, 10));
shapes.add(new Rectangle(6, 6));
shapes.add(new Circle(6));
shapes.add(new Square(5));
shapes.add(new Square(9));
AreaManager.computeArea(shapes);
}