走向面向对象的六大原则--迪米特原则

面向对象编程的六大原则

  • 单一职责原则
  • 开闭原则
  • 里氏替换原则
  • 依赖倒置原则
  • 接口隔离原则
  • 迪米特原则

更好的可拓展性--迪米特原则
迪米特原则的英文全称是Law of Demeter,缩写是LOD,也称为最少知识原则。
定义是:一个对象应该对其他对象有最少的了解。
直白的说,就是让对象只与最直接的朋友进行联系。那么什么是最直接朋友呢?每个对象都必然会与其他类有耦合关系,两个对象之间互相耦合就为朋友关系,这种关系类型有很多,比如组合,聚合,依赖。
下面,我们就通过举例来说明迪米特原则

相信在外务工的朋友,都有租房的经历,一般情况下大家都会通过中介找房。我们通过租房进行举例,我们设定条件为:我只要求房子的租金和面积,中介负责将合适的房子介绍给我。以下是代码实例。

// 房子
public class House {
    public float area;
    public float price;

    public House(float area, float price) {
        this.area = area;
        this.price = price;
    }

    @Override
    public String toString() {
        return "House{" +
                "area=" + area +
                ", price=" + price +
                '}';
    }
}
// 中介
public class Agency {
    List mHouses = new ArrayList<>();

    public Agency() {
        for (int i = 0; i < 5; i++) {
            mHouses.add(new House(100 + i, (100 + i) * 200));
        }
    }

    public List getHouses() {
        return mHouses;
    }
}
//租户
public class Tenant {
    public float houseArea;
    public float housePrice;
    public static final float diffPrice = 200.001f;
    public static final float diffArea = 0.00001f;

    public void rentHouse(Agency agency) {
        List mHouses = agency.getHouses();
        for (House house : mHouses) {
            if (isRightHouse(house)) {
                System.out.println("找到合适的房子了!\n" + house.toString());
                break;
            }

        }
    }

    private boolean isRightHouse(House house) {
        return Math.abs(house.price - housePrice) < diffPrice
                && Math.abs(house.area - houseArea) < diffArea;
    }
}

从上面的代码我们可以看到,我们的租户类(Tenant)不仅依赖了中介类(Agency),还要频繁地与我们的房子类(House)打交道。我们的需求是:我只要求房子的租金和面积,中介负责将合适的房子介绍给我。但是现在是我们拿到了一大堆房子数据然后自己进行分析和计算,这样一来,中介的功能就被弱化了,并且导致我们的租户类(Tenant)和房子类(House)的耦合过高,因为作为租户而言,我们必须知道房子(House)的细节。而此时租户类(Tenant)又和中介类(Agency)保持联系。这个时候我们就有必要进行一定的代码调整,去区分清楚,究竟谁是谁的直接朋友,让我们的代码清晰起来。
我们通过需求进行分析,可以得出以下几点:

  • 租户只负责租房,提出房子的大小要求和租金要求,其他的要交给中介来做。
  • 租户不参与房子的分析比对。
  • 中介负责与客户进行交流,并且为客户进行房子的比对和筛选。

如此一来,我们可以根据需求对代码进行如下调整:

public class Agency {
    List mHouses = new ArrayList<>();

    public Agency() {
        for (int i = 0; i < 5; i++) {
            mHouses.add(new House(100 + i, (100 + i) * 200));
        }
    }

    public House rentHouse(float area, float price) {
        for (House house : mHouses) {
            if (isRightHouse(area, price, house)) {
                System.out.println("找到合适的房子了!\n" + house.toString());
                return house;
            }
        }
        return null;
    }

    private boolean isRightHouse(float area, float price, House house) {
        return Math.abs(house.price - price) < Tenant.diffPrice
                && Math.abs(house.area - area) < Tenant.diffArea;
    }
}

public class Tenant {
    public float houseArea;
    public float housePrice;
    public static final float diffPrice = 200.001f;
    public static final float diffArea = 0.00001f;

    public void rentHouse(Agency agency) {
        System.out.println("找到合适的房子了:" + agency.rentHouse(houseArea, housePrice));
    }
}

此处我们经过一些简单的调整,将对房子是否符合要求的判断放到了中介里,这其实本就应该是中介的职责,根据用户设定的条件去寻找房子,找到了就告诉用户就可以了。而用户也不需要知道有多少房源,每个房子是什么样,他只需要询问中介,中介告知结果即可。
从上述代码我们可以看到,我们通过一次简单的代码修改后,代码的耦合性已经降低了很多,同时,他们都达成了与最直接的朋友联系的条件,将各个角色从复杂的关系中抽离出来,使程序的耦合更低,稳定性更好。
这里我们再次回忆一下我们ImageLoader中的代码,就拿SD卡缓存来说,ImageCache就是用户的直接朋友,而SD卡缓存内部却是使用了DiskCache实现,这个DiskCahce就不是用户的直接朋友了,因此,其实用户完全不需要知道有这样的缓存存在,用户只需要和ImageCache打交道就好了,我们之前的代码我再次优化过,使用了JakeWharton的DiskLruCache,最后代码如下:

public void put(String url, Bitmap bitmap) {
    DiskLruCache.Editor editor = null;
    //如果没有在内存缓存和磁盘缓存中找到对对应的缓存,从网络上请求数据
    //写入缓存
    editor = mDiskLruCache.edit(url);
    try {
        if (editor != null) {
            OutputStream out = editor.newOutputStream(0);
            if (writeBitmapToDisk(value, out)) {
                //写入SD卡
                editor.commit();
            } else {
                editor.abort();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    CloseUtils.colse(out);
}

用户在使用SD卡缓存的时候,根本不知道DiskLruCache的实现,这就很好地对用户隐藏了具体的代码实现。当你能够自己去写DiskLruCache的时候,你就可以随意替换掉此处我们引用的JakeWharton大神的DiskLruCache了。


总结#

一不小心,六个章节的《走向面向对象的六大原则》就到此结束了,在这里感谢何红辉、关爱民先生所著的《Android设计模式》一书,给我了很多启发和灵感,并且示例上提供了很多借鉴。
时间总是很快,快到你都注意不到自己的胡渣子又冒出来了,在我们开发的过程中,最难的不是开发出用户所需要的功能,而是在后续的升级和维护过程中,让系统能够拥抱变化,不因为版本的迭代更新而破坏模块的完整性和代码的条理性,保证代码的低耦合,高可拓展性,高内聚,在经理了N个版本的迭代以后,软件系统依旧能够保持稳健,灵活,清晰的架构。
当然啦,这只是一个理想的情况,代码迭代而引发的坑,我们爬的还少吗?为了少爬坑,为了让自己的代码更漂亮,系统更稳定,我们必须朝着这个方向去努力,遵循面向对象的六大原则,就是我们走出的,第一步
明天的将会写关于单例模式的一些理解和示例,感谢大家的一直支持,我们共同进步!
谢谢。

你可能感兴趣的:(走向面向对象的六大原则--迪米特原则)