一.什么是程序的耦合?

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。

二.软件工程中耦合的分类

1.内容耦合

①概念:

当一个模块直接修改或操作另一个模块的数据时,或者一个模块不通过正常的入口而转入另一个模块时,被称为内容耦合.

②示例:

***A.java

public class A {
    Integer id;
}

***B.java

public class B {
    A a = new A();
    public int add(){
        a.id= 100;
        return a.id++;
    }

}

③分析:

当A类中的属性id名称修改时,B类中的a.id会发生编译异常

④解决:(必须解决)

为A类的属性id增加setter&getter方法,在B类中通过id的setter&getter方法对属性id进行设置

2.公共耦合

①概念:

两个或者两个以上的模块共同引用一个全局数据项,被称之为公共耦合

②示例:

***A.java

public class A {
    public void read()throws Exception{
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("a.txt");
        byte[] bytes = new byte[in.available()];
        in.read(bytes);
        in.close();
        String str = new String(bytes,"UTF-8");
        System.out.println(str);
    }
    public static void main(String[] args) {
        A a = new A();
        try {
            a.read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

***B.java

public class B {
    public void read()throws Exception{
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("a.txt");
        byte[] bytes = new byte[in.available()];
        in.read(bytes);
        in.close();
        String str = new String(bytes,"UTF-8");
        System.out.println(str);
    }
    public static void main(String[] args) {
        B b = new B();
        try {
            b.read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

***C.java

public class C {

    public void read()throws Exception{
        InputStream in = this.getClass().getClassLoader().getResourceAsStream("a.txt");
        byte[] bytes = new byte[in.available()];
        in.read(bytes);
        in.close();
        String str = new String(bytes,"UTF-8");
        System.out.println(str);
    }

    public static void main(String[] args) {
        C c = new C();
        try {
            c.read();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

③分析:

ABC三个类通过都对文件a.txt进行了读取,做了相同的操作,代码重复且冗余

在idea中的 表现是这部分重复的代码有波浪线标识

④解决:(必须解决)

将ABC中相同的代码抽取出来作为公共调用方法

3.控制耦合/外部耦合

①概念:

一组模块都访问同一个全局简单变量,而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,被称为外部耦合.

一个模块通过接口向另一个模块传递一个控制信号,接收信号的模块根据信号进行适当的动作,被称之为控制耦合

②示例:

public class A {
    public String txType;
    public void setTxType(Boolean con){
        if(con){
            txType = "autoCommit";
        }else{
            txType = "not autoCommit";
        }
    }
    public void commit(){
        if(txType.equalsIgnoreCase("autoCommit")){
            System.out.println("设置自动提交事务");
            System.out.println("开始执行设置....");
        }else{
            System.out.println("已设置手动提交!");
        }
    }
}

③分析:

当变量txType的名称发生变化时,两个方法都会报错,这种耦合叫外部耦合

setTxType()中对txType的属性值设置,会影响到commit()的逻辑走向,这种耦合叫控制耦合

④解决:

不必解决

4.标记耦合

①概念:

指两个模块之间传递的是数据结构,如高级语言的数组名、记录名、文件名等这些名字即为标记,其实传递的是这个数据结构的地址

②示例:

***User.java

public class User {

    private Integer id;         // 用户ID
    private String realName;    // 真实姓名
    private String level;       // 用户等级
    private String tel;         // 电话
    private String emial;       // 电子邮箱
    private Double salPoint;    // 积分

    public Integer getId() {
        return id;
    }

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

    public String getRealName() {
        return realName;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getEmial() {
        return emial;
    }

    public void setEmial(String emial) {
        this.emial = emial;
    }

    public Double getSalPoint() {
        return salPoint;
    }

    public void setSalPoint(Double salPoint) {
        this.salPoint = salPoint;
    }
}

***Order.java

public class Order {

    public Double getTotalPrice(User user){
        // 创建方法的返回值
        Double totalPrice = 0D;
        // 获取积分和会员等级计算消费金额及折扣
        String level = user.getLevel();
        Double score = user.getSalPoint();
        .......
        具体扣除逻辑代码略
        .......
        return totalPrice;
    }
}

③分析:

订单中计算预付款金额时需要根据用户的会员等级以及其积分情况进行优惠处理

这里getTotalPrice()方法的形参为User对象,相当于将User的全部信息在Order内暴露出来了

当修改User的时候还需要考虑到Order会不会受到影响

④解决:

传递的参数不宜为对象,可以更换为(String level,String,score)

5.数据耦合

①概念:

模块之间通过参数进行传递,被称之为数据耦合

②示例:

public class A {

    public int jiafa(int a,int b){
        return a+b;
    }

    public void main(){
        int a = 1;
        int b = 1;
        System.out.println("a+b=" + jiafa(a,b));
    }
}

③分析:

当jiafa()的形参数据结构变为(char a,int b)时,main方法中方法调用处会报错

④解决:

不必要解决

6.非直接耦合

①概念:

两个模块之间没有直接的关系,他们之间的联系是通过主模块的控制和调用来实现的

②示例:

***Sheep.java

public class Sheep {

    private String name;
    private Integer count;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }
}

***Vegetables.java

public class Vegetables {

    private String type;
    private Integer count;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }
}

***Main.java

public class Main {

    public void eatHotPot(){

        Sheep p1 = new Sheep();
        p1.setName("羊里脊肉");
        p1.setCount(2);

        Vegetables v1 = new Vegetables();
        v1.setType("油麦菜");
        v1.setCount(1);
    }
}

③分析:

羊肉和蔬菜没啥关系,只是因为我吃了一顿火锅,所以它们就跑到一个锅里去了

④解决:

不必要解决