目录

一、设计目的

二、七大原则

(一)单一职责原则 SRP(Single Responsibility Principle)

(二)接口隔离原则 ISP(Interface Segregation Principle)

(三)依赖倒转原则 DIP (Dependence Inversion Principle)

(四)里氏替换原则 LSP (Liskov Subsitution Principle)

(五)开闭原则 OCP (Open Closed Principle)

(六)迪米特法则 DP (Demeter Principle)

(七)合成复用原则 CRP(Composite Reuse Principle)

三、小结


一、设计目的

1. 重用性(即:相同功能的代码,不用多次编写)

2. 可读性(即:编程规范性,便于其他程序员的阅读和理解)

3. 可扩展性(即:当要增加新的功能时,非常的方便.称为可维护)

4. 可靠性(即:当我们增加新的功能后,对原来的功能没有影响)

5. 使程序呈现高内聚.低耦合的特性

二、七大原则

(一)单一职责原则 SRP(Single Responsibility Principle)


一个类应该只负责一项职责。


低级代码(当用成杜鹃,显然就不合适了):


public class SingleResponsibility1 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.eat("鲨鱼");
        animal.eat("杜鹃");
    }
}
class Animal{
    public void eat(String animalName){
        System.out.println(animalName + "在海里进食");
    }
}

应用原则后:

public class SingleResponsibility2 {
    public static void main(String[] args) {
        new Fish().eat("鲨鱼");
        new Bird().eat("杜鹃");
    }
}

class Fish {
    public void eat(String animalName) {
        System.out.println(animalName + "在海里进食");
    }
}

class Bird {
    public void eat(String animalName) {
        System.out.println(animalName + "在陆地上进食");
    }
}

突出作用:



1) 降低类的复杂度,一个类只负责一项职责。



2) 提高类的可读性,可维护性



3) 降低变更引起的风险(修改只改动对应的类以及对应的方法)



4) 通常情况下, 我们应当遵守单一职责原则 ,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则


(二)接口隔离原则 ISP(Interface Segregation Principle)


一个类对另一个类的依赖,(中间)应该建立在最小的接口上


低级代码:

public class InterfaceSegregationPrinciple1 {
    public static void main(String[] args) {
        OneMan m = new OneMan();
        m.show(new Man());
        OneWoman w = new OneWoman();
        w.show(new Woman());
    }
}
interface IBuy {
    void buySkirt();
    void buyPants();
    void buyDress();
}
class Man implements IBuy {
    @Override
    public void buySkirt() {
        System.out.println("男士买衬衫");
    }

    @Override
    public void buyPants() {
        System.out.println("男士买裤子");
    }

    @Override
    public void buyDress() {
        System.out.println("男士买裙子");
    }

}

class Woman implements IBuy {
    @Override
    public void buySkirt() {
        System.out.println("女士买衬衫");
    }

    @Override
    public void buyPants() {
        System.out.println("女士买裤子");
    }

    @Override
    public void buyDress() {
        System.out.println("女士买裙子");
    }

}
class OneMan{
    public void buySkirt(IBuy buy){
        buy.buySkirt();
    }
    public void buyPants(IBuy buy){
        buy.buyPants();
    }
}
class OneWoman{
     public void buyDress(IBuy buy){
        buy.buyDress();
    }
    public void buySkirt(IBuy buy){
        buy.buySkirt();
    }
}

应用原则后(OneMan只需要买衬衫和裤子即可,所以Man类无需多实现个买裙子的方法;Woman类同理。但因为实现Buy接口,所以抽象方法都得实现,故拆分接口):

package com.principle.interfaceSegregationPrinciple;

public class InterfaceSegregationPrinciple2 {
    public static void main(String[] args) {
        OneMan2 m = new OneMan2();
        m.buyPants(new Man2());
        m.buySkirt(new Man2());
        //
        OneWoman2 w = new OneWoman2();
        w.buyDress(new Woman2());
        w.buySkirt(new Woman2());
    }
}

interface IBuyWomanClothes {
    void buyDress();
}
interface IBuyManClothes{
    void buyPants();
}
interface IBuyCommonClothes{
    void buySkirt();
}

class Man2 implements IBuyManClothes, IBuyCommonClothes {
    @Override
    public void buySkirt() {
        System.out.println("男士买衬衫");
    }

    @Override
    public void buyPants() {
        System.out.println("男士买裤子");
    }

}

class Woman2 implements IBuyWomanClothes , IBuyCommonClothes {
    @Override
    public void buySkirt() {
        System.out.println("女士买衬衫");
    }

    @Override
    public void buyDress() {
        System.out.println("女士买裙子");
    }

}

class OneMan2{
    public void buySkirt(IBuyCommonClothes buy){
        buy.buySkirt();
    }
    public void buyPants(IBuyManClothes buy){
        buy.buyPants();
    }
}

class OneWoman2{
    public void buyDress(IBuyWomanClothes buy){
        buy.buyDress();
    }
    public void buySkirt(IBuyCommonClothes buy){
        buy.buySkirt();
    }
}

效果如下:

java的设计背景 java设计目的和意义_java的设计背景

拆成

java的设计背景 java设计目的和意义_ide_02

(三)依赖倒转原则 DIP (Dependence Inversion Principle)


1) 高层模块不应该依赖低层模块,二者都应该依赖其抽象


2) 抽象不应该依赖细节,细节应该依赖抽象


3) 依赖倒转 ( 倒置 ) 的中心思想是面向接口编程


4) 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的 。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类


5) 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成


 

我的理解是:依赖倒转,把本来在封装中的类改为接口,而在入口(调用封装方法)的地方使用具体想要的类。

低级代码:

public class DependenceInversionPrinciple1 {
    public static void main(String[] args) {
        new Developer().setMessage(new WeiXinSmallApp());
    }
}
class WeiXinSmallApp{
    public void showMessage(){
        System.out.println("微信小程序新活动");
    }

}
class Developer{
    // 该方法的参数列表就直接依赖WeiXinSmallApp类,这使得不适用于其他类
    public void setMessage(WeiXinSmallApp weiXinSmallApp){
        weiXinSmallApp.showMessage();
    }
}

应用原则后(依赖倒转有三种实现方式,1) 接口传递 2) 构造方法传递 3) setter方式传递 。分别如下):

// ① 在封装方法的参数列表中使用接口
public class DependenceInversionPrinciple2 {
    // 依赖倒转,把本来在封装中的类改为接口,而在入口(调用封装方法)的地方使用具体想要的类。
    public static void main(String[] args) {
        Developer2 developer2 = new Developer2();
        developer2.setMessage(new WeiXinSmallApp2());
        developer2.setMessage(new AndroidApp());
    }
}

class WeiXinSmallApp2 implements IProduct {
    @Override
    public void showMessage() {
        System.out.println("微信小程序新活动");
    }
}
class AndroidApp implements IProduct{

    @Override
    public void showMessage() {
        System.out.println("安卓App新活动");
    }
}

interface IProduct {
    void showMessage();
}

class Developer2 {
    public void setMessage(IProduct iProduct) {
        iProduct.showMessage();
    }
}
// ② 在封装方法声明接口变量,在构造器中赋值接口,在方法中调用接口变量
public class DependenceInversionPrinciple2 {
    // 依赖倒转,把本来在封装中的类改为接口,而在入口(调用封装方法)的地方使用具体想要的类。
    public static void main(String[] args) {
        new Developer2(new WeiXinSmallApp2()).setMessage();
        new Developer2(new AndroidApp()).setMessage();
    }
}

class WeiXinSmallApp2 implements IProduct {
    @Override
    public void showMessage() {
        System.out.println("微信小程序新活动");
    }
}
class AndroidApp implements IProduct{

    @Override
    public void showMessage() {
        System.out.println("安卓App新活动");
    }
}

interface IProduct {
    void showMessage();
}

class Developer2 {
    // 这么做就从方法的范围跳到成员变量的范围(其他方法也可以使用)
    IProduct iProduct;
    public Developer2(IProduct iProduct) {
        this.iProduct = iProduct;
    }

    public void setMessage() {
        this.iProduct.showMessage();
    }
}
// ③ 在封装方法声明接口变量,在setter中赋值接口,在方法中调用接口变量
public class DependenceInversionPrinciple2 {
    // 依赖倒转,把本来在封装中的类改为接口,而在入口(调用封装方法)的地方使用具体想要的类。
    public static void main(String[] args) {
        Developer2 developer2 = new Developer2();
        developer2.setIProduct(new WeiXinSmallApp2());
        developer2.setMessage();
        //
        developer2.setIProduct(new AndroidApp());
        developer2.setMessage();
    }
}

class WeiXinSmallApp2 implements IProduct {
    @Override
    public void showMessage() {
        System.out.println("微信小程序新活动");
    }
}
class AndroidApp implements IProduct{

    @Override
    public void showMessage() {
        System.out.println("安卓App新活动");
    }
}

interface IProduct {
    void showMessage();
}

class Developer2 {
    // 这么做就从方法的范围跳到成员变量的范围(其他方法也可以使用)
    IProduct iProduct;

    public void setIProduct(IProduct iProduct) {
        this.iProduct = iProduct;
    }

    public void setMessage() {
        this.iProduct.showMessage();
    }
}


1) 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好 .


2) 变量的声明类型尽量是抽象类或接口 , 这样我们的变量引用和实际对象间,就存在 一个缓冲层,利于程序扩展和优化


3) 继承时遵循里氏替换原则


 


 


(四)里氏替换原则 LSP (Liskov Subsitution Principle)


1) 继承中,父类中凡是已经实现好的方法,实际上是在设定规范和契 ,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实 现的方法任意修改,就会对整个继承体系造成破坏



 



2) 使用继承会给程序带来侵 入性,程序的可移植性降低增加对象间的耦合性如果一个类被其他的类所继承, 则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子 类的功能都有可能产生故障



 



3) 【其要求:在父类构成的程序中,将父类对象替换为子类对象,程序的行为不变。】



 



4)在子类中 尽量不要重写父类的方法 



 



5)继承实际上让两个类耦合性增强了, 在适当的情况下,可 以通过聚合,组合,依赖 来解决问题。


 


低级代码:


public class LiskovSubsitutionPrinciple1 {
    public static void main(String[] args) {
        WeiXinAlgorithm weiXinAlgorithm = new WeiXinAlgorithm();
        weiXinAlgorithm.oneAlgorithm();
    }
}
class QQAlgorithm{
    public void oneAlgorithm(){
        System.out.println("一个不错的算法");
    }
}
class WeiXinAlgorithm extends QQAlgorithm{
    @Override
    public void oneAlgorithm(){
        System.out.println("算法稍微改改");
    }
}

应用原则后(将原子类与原父类平级,共同继承基类):

public class LiskovSubsitutionPrinciple2 {
    public static void main(String[] args) {
        WeiXinAlgorithm weiXinAlgorithm = new WeiXinAlgorithm();
        weiXinAlgorithm.oneAlgorithm();
    }
}
class BaseAlgorithm{
    // 写共同的基础的代码
}

class QQAlgorithm2 extends BaseAlgorithm{
    public void oneAlgorithm(){
        System.out.println("一个不错的算法");
    }
}

class WeiXinAlgorithm2 extends BaseAlgorithm{
    private QQAlgorithm2 qqAlgorithm2 = new QQAlgorithm2(); // 组合代替继承,耦合性降低
    public void oneAlgorithm(){
        qqAlgorithm2.oneAlgorithm();
        // 继续改良
    }
}



1) 我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。



2) 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉, 采用依赖,聚合,组合等关系代替 .


 


(五)开闭原则 OCP (Open Closed Principle)


1) 开闭原则 是编程中最基础、最重要的设计原则



2) 一个软件实体如类,模块和函数应该对扩展开放 (对提供方),对修改关闭(对使用方) 。用抽象构建框架,用实现扩展细节。



3) 当软件需要变化时,尽量 通过扩展 软件实体的行为来实现变化,而 不是通过修改 已 有的代码来实现变化。



4) 编程中遵循其它原则,以及使用设计模式的目的就是遵循 开闭原则



5)当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码



 



低级代码:



package com.principle.openClosed;

public class OpenClosedPrinciple1 {
    public static void main(String[] args) {
        ShoppingList shoppingList = new ShoppingList();
        shoppingList.addGood(new AddDesk());
        shoppingList.addGood(new AddChair());
    }
}
// 购物清单【使用方】
class ShoppingList{
    public void addGood(AddGood addGood){
        if(addGood.add_type == 1){
            addOneDesk();
        }else if(addGood.add_type == 2){
            addOneChair();
        }
        // 再添加别的货物需要再添加判断
    }

    public void addOneDesk(){
        System.out.println("添加一张桌子");
    }
    public void addOneChair(){
        System.out.println("添加一把椅子");
    }
    // 再添加别的货物需要再添加方法
}
class AddGood {
    int add_type;
}
class AddDesk extends AddGood {
    public AddDesk() {
        add_type = 1;
    }
}
class AddChair extends AddGood {
    public AddChair() {
        add_type = 2;
    }
}

应用原则后:

package com.principle.openClosed;

public class OpenClosedPrinciple2 {
    public static void main(String[] args) {
        ShoppingList2 shoppingList2 = new ShoppingList2();
        shoppingList2.addGood(new AddDesk2());
        shoppingList2.addGood(new AddChair2());
    }
}

// 购物清单【使用方】
class ShoppingList2{
    // 此时使用方不需要变动修改
    public void addGood(AddGood2 addGood2){
        addGood2.addGood();
    }
}

abstract class AddGood2 {
    abstract void addGood();
}


class AddDesk2 extends AddGood2 {
    @Override
    void addGood() {
        System.out.println("添加一张桌子");
    }
}

class AddChair2 extends AddGood2 {
    @Override
    void addGood() {
        System.out.println("添加一把椅子");
    }
}

思路:抽象类提供一个抽象方法,让子类去实现即可。

(六)迪米特法则 DP (Demeter Principle)



1) 一个对象应该对其他对象保持最少的了解



2) 类与类关系越密切,耦合度越大



3) 迪米特法则 ( Demeter Principle ) 又叫 最少知道原则 ,即一个类对自己依赖的类知道的 越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息



4) 迪米特法则还有个更简单的定义:只与直接的朋友通信



5) 直接的朋友 :每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系, 我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部(应该封装进方法,以public提供给使用方)



 



低级代码:



 



package com.principle.demeter;

import java.util.ArrayList;
import java.util.List;

public class DemeterPrinciple1 {
    public static void main(String[] args) {
        // 模拟打印清单
        ShoppingList shoppingList = new ShoppingList();
        shoppingList.addList();
        new Machine().printShoppingList(shoppingList);
    }


}
class Good{
    int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
class ShoppingList{
    private List<Good> goodList = new ArrayList<Good>();
    public void addList() {
        for(int i = 1; i < 10; i++){
            Good good = new Good();
            good.setId(i);
            goodList.add(good);
        }
    }
    public List<Good> getGoodList(){
        return goodList;
    }
}
class Machine{
    // 收银机打印
    public void printShoppingList(ShoppingList shoppingList){
        // 这里Good类就是Machine类的陌生类(局部变量)
        List<Good> goodList = shoppingList.getGoodList();
        for(Good good : goodList){
            System.out.println("商品" + good.getId());
        }
    }
}

应用原则后:

package com.principle.demeter;


import java.util.ArrayList;
import java.util.List;

public class DemeterPrinciple2 {
    public static void main(String[] args) {
        // 模拟打印清单
        ShoppingList2 shoppingList2 = new ShoppingList2();
        shoppingList2.addList();
        new Machine2().printShoppingList(shoppingList2);
    }


}

class Good2 {
    int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

class ShoppingList2{
    private List<Good2> goodList = new ArrayList<Good2>();
    public void addList() {
        for(int i = 1; i < 10; i++){
            Good2 good2 = new Good2();
            good2.setId(i);
            goodList.add(good2);
        }
    }
    public void printShoppingList(){
        for(Good2 good2 : goodList){
            System.out.println("商品" + good2.getId());
        }
    }
    public List<Good2> getGoodList(){
        return goodList;
    }
}

class Machine2{
    // 收银机打印
    public void printShoppingList(ShoppingList2 shoppingList2){
        // 直接把细节封装在ShoppingList2类中
        shoppingList2.printShoppingList();
    }
}



1) 按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。


 


2) 迪米特法则的核心是 降低类之间的耦合


 


(七)合成复用原则 CRP(Composite Reuse Principle)


1) 原则是尽量使用合成 /聚合的方式,而不是使用继承(如左上依赖,左下聚合,右边组合)


 




java的设计背景 java设计目的和意义_System_03


 


1) 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代


码混在一起


2) 针对接口编程,而不是针对实现编程。


3) 为了交互对象之间的松耦合设计而努力



三、小结

单一职责:考虑这个类还能不能拆
接口隔离:接口实现类不需要接口的某些方法,则拆接口
依赖倒转:用抽象声明变量,具体留给调用方
里氏替换:将原子类与原父类平级,共同继承基类,现子类聚合/组合/依赖现父类
开放封闭:抽象代替变化,扩展代替修改
迪米特:局部变量无陌生类
合成复用:聚合/组合/依赖代替继承