面向对象五大基本原则(一)

前言

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);
    }

总结:在开发中,对新功能加以灵活扩展,不修改已有的代码

Ⅲ.总结

原本打算一篇博文总结完面向对象五大基本原则,后面写了上面的这两个原则才发现篇幅已经如此这般了,再写下去就不一般了。为了避免阅读的良好性,所以剩下的三大原则再给总结一篇,也不晓得什么时候能总结完成,好吧!升懒腰先~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章