面向對象五大基本原則(一)

前言

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

總結:在開發中,對新功能加以靈活擴展,不修改已有的代碼

Ⅲ.總結

原本打算一篇博文總結完面向對象五大基本原則,後面寫了上面的這兩個原則才發現篇幅已經如此這般了,再寫下去就不一般了。爲了避免閱讀的良好性,所以剩下的三大原則再給總結一篇,也不曉得什麼時候能總結完成,好吧!升懶腰先~~

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