zl程序教程

您现在的位置是:首页 >  其他

当前栏目

软件7大设计原则(附案例所敲代码)

案例软件代码 设计 原则
2023-06-13 09:14:41 时间

目录

1 、开闭原则

2、单一职责原则

3、里氏替换原则

3.1 使用前

3.1、使用后

4、依赖倒转原则

4.1、使用前

4.2 、使用后

5、接口隔离原则

5.1、使用前

5.2、使用后

6、迪米特法则

7、合成复用原则

8、本篇博客所写代码


在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据软件设计原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。

1 、开闭原则

概念: 开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则; 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。 想要达到这样的效果,我们需要使用接口和抽象类。 因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

案例: 需求:给英雄盖伦切换皮肤

代码:

package com.jie.principles.lockage;
/**
 * @description:抽象皮肤类
 * @author: jie 
 * @time: 2022/1/28 11:00
 */
public abstract class Skin {

    /**
     * @description:显示皮肤
     * @author: jie
     * @time: 2022/1/28 12:18
     */
    public abstract void display();

}
package com.jie.principles.lockage;
/**
 * @description:默认皮肤德玛西亚之力
 * @author: jie
 * @time: 2022/1/28 12:02
 */
public class DefaultSkin extends Skin{

    @Override
    public void display() {
        System.out.println("德玛西亚之力:盖伦");
    }
}
package com.jie.principles.lockage;
/**
 * @description:战斗学院
 * @author: jie
 * @time: 2022/1/28 12:05
 */
public class BattleAcademy extends Skin{
    @Override
    public void display() {
        System.out.println("战斗学院:盖伦");
    }
}
package com.jie.principles.lockage;
/**
 * @description:盖伦
 * @author: jie 
 * @time: 2022/1/28 12:07
 */
public class Garen {

    /**
     * @description:皮肤
     * @author: jie
     * @time: 2022/1/28 12:11
     */
    private Skin skin;

    public Skin getSkin() {
        return skin;
    }

    public void setSkin(Skin skin) {
        this.skin = skin;
    }

    
    public void display(){
        skin.display();
    }
}
package com.jie.principles.lockage;

/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 12:12
 */
public class Test {

    public static void main(String[] args) {
        //默认皮肤
        defaultSkin();
        //战斗学院皮肤
        battleAcademy();

    }

    /**
     * @description:默认皮肤
     * @author: jie
     * @time: 2022/1/28 12:27
     */
    public static void defaultSkin() {
        //1、创建盖伦对象
        Garen garen = new Garen();
        //2、创建默认皮肤对象
        DefaultSkin defaultSkin = new DefaultSkin();
        //3、将皮肤设置给盖伦
        garen.setSkin(defaultSkin);
        //4、显示皮肤
        garen.display();
    }

    /**
     * @description:战斗学院
     * @author: jie
     * @time: 2022/1/28 12:31
     */
    public static void battleAcademy() {
        //1、创建盖伦对象
        Garen garen = new Garen();
        //2、创建战斗皮肤对象
        BattleAcademy battleAcademy = new BattleAcademy();
        //3、将皮肤设置给盖伦
        garen.setSkin(battleAcademy);
        //4、显示皮肤
        garen.display();
    }

}

测试结果

需求发生变更:此时需要新增盖伦的新皮肤神王  

package com.jie.principles.lockage;
/**
 * @description:神王
 * @author: jie
 * @time: 2022/1/28 12:47
 */
public class Gods extends Skin{

    @Override
    public void display() {
        System.out.println("神王:盖伦");
    }
}
package com.jie.principles.lockage;

/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 12:12
 */
public class Test {

    public static void main(String[] args) {
        //神王
        gods();
    }
    
    /**
     * @description:神王
     * @author: jie
     * @time: 2022/1/28 12:49
     */
    public static void gods() {
        //1、创建盖伦对象
        Garen garen = new Garen();
        //2、创建神王皮肤对象
        Gods gods = new Gods();
        //3、将皮肤设置给盖伦
        garen.setSkin(gods);
        //4、显示皮肤
        garen.display();
    }
}

测试结果:

小结: 遵循了开闭原则,Skin作为主体的类无需修改;只需要扩展其子类即可; 这就是“对扩展开放,对修改关闭”的含义;

2、单一职责原则

概念 降低类的复杂度,一个类只负责一项职责 提高类的可读性,可维护性 降低变更引起的风险,对于服务端的代码尽量做到只新增不修改

上面皮肤案例也体现了单一职责原则:

使用单一职责后:皮肤交给各个子类去实现,一个类 负责一款皮肤;

3、里氏替换原则

概念: 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则:任何基类可以出现的地方,子类一定可以出现。 通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。 换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。 如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。 在适当的情况下,可以通过聚合,组合,依赖 来解决问题

案例: 需求:完成两个数的减法运算,封装成计算器;计算器的种类有很多种( 算术型计算器 、科学型计算器、程序计算器....)

3.1 使用前

先简单的来实现算数型计算器的减法运算功能;

package com.jie.principles.Richter;
/**
 * @description:所有计算机的父类
 * @author: jie
 * @time: 2022/1/28 13:49
 */
public abstract class Cal {
    protected abstract double minus(double x, double y);
}
package com.jie.principles.Richter;
/**
 * @description:最基础的算数型的计算器
 * @author: jie
 * @time: 2022/1/28 13:53
 */
class BaseCountCal extends Cal {

    @Override
    protected double minus(double x, double y) {
        return x - y;
    }
}
package com.jie.principles.Richter;
/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 13:55
 */
public class Test {
    public static void main(String[] args) {
        Cal cal = new BaseCountCal();
        //成6与3的减法
        System.out.println(cal.minus(6, 3));
    }
}

测试结果:

需求变更,需要做一个程序计算器,完成两个数的减法

/**
 * @description:完成程序计算器的减法
 * @author: jie
 * @time: 2022/1/28 14:05
 */
class ProcessCal extends Cal{

    @Override
    protected double minus(double x, double y) {
        return x+y;
    }
}
package com.jie.principles.Richter;

/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 13:55
 */
public class Test {
    public static void main(String[] args) {
        Cal cal = new BaseCountCal();
        //完成6与3的减法(基本算数计算器)
        System.out.println(cal.minus(6, 3));
        //完成6与3的减法(程序计算器)
        cal = new ProcessCal();
        System.out.println(cal.minus(6, 3));
    }
}

从上面可以看出 不遵循里氏替换原则:Cal定义的两个数的减法,直接被子类ProcessCal给覆写了,覆写的结果导致调用方Test使用的时候未能得到自己想要结果;原因在于该程序类与类之间的耦合性高(继承关系)

3.1、使用后

package com.jie.principles.Richter;
/**
 * @description:完成程序计算器的减法
 * @author: jie
 * @time: 2022/1/28 14:05
 */
class ProcessCal extends Cal{

    /**
     * @description:聚合Cal抽象类,不变的引用它,变化的不引用
     * @author: jie
     * @time: 2022/1/28 14:33
     */
    private Cal cal;

    public ProcessCal(Cal cal) {
        this.cal = cal;
    }

    @Override
    protected double minus(double x, double y) {
        return cal.minus(x, y) ;
    }
}
package com.jie.principles.Richter;

/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 13:55
 */
public class Test {
    public static void main(String[] args) {
        Cal cal = new BaseCountCal();
        //完成6与3的减法(基本算数计算器)
        System.out.println(cal.minus(6, 3));

        //完成6与3的减法(程序计算器)
        ProcessCal processCal = new ProcessCal(cal);
        System.out.println(processCal.minus(6, 3));
    }
}

测试结果:

遵循里氏替换原则:Cal定义的两个数的减法,ProcessCal类的多数行为(加乘除)与Cal类一致,此时应该使用聚合/组合的方式,这样的话解耦了ProcessCal与Cal之间的关系,调用方Test使用减法运算的时候会更加关注ProcessCal减法的实现过程; 这也就是里氏替换所说的,建议使用聚合/组合的方式来解决依赖关系,子类尽量不要覆写父类的方法;

4、依赖倒转原则

高层模块不应该依赖低层模块,二者都应该依赖其抽象。 抽象不应该依赖细节,细节应该依赖抽象。 依赖倒转(倒置)的中心思想是面向接口编程。 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。

案例:

需求:老师通过不同的APP与学生聊天

实现老师通过微信、扣扣的方式,完成与学生互动需求;

4.1、使用前

package com.jie.principles.relyonreverse;
/**
 * @description:微信
 * @author: jie
 * @time: 2022/1/28 15:31
 */
class WeiXin{
    public String info;

    public String getInfo() {
        return info;
    }

    public WeiXin(String info) {
        this.info = info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:QQ
 * @author: jie
 * @time: 2022/1/28 15:31
 */
class QQ{
    public String info;

    public String getInfo() {
        return info;
    }

    public QQ(String info) {
        this.info = info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:老师
 * @author: jie
 * @time: 2022/1/28 15:30
 */
public class Teacher {
    public void receiveWeiXin(WeiXin weiXin){
        System.out.println(weiXin.getInfo());
    }

    public void receiveQQ(QQ qq){
        System.out.println(qq.getInfo());
    }
}

需求变更:老师新增了一个渠道“钉钉”获取学生的信息

package com.jie.principles.relyonreverse;
/**
 * @description:钉钉
 * @author: jie
 * @time: 2022/1/28 15:36
 */
class DingDing{
    private String info;

    public String getInfo() {
        return info;
    }

    public DingDing(String info) {
        this.info = info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:老师
 * @author: jie
 * @time: 2022/1/28 15:30
 */
public class Teacher {
    public void receiveWeiXin(WeiXin weiXin){
        System.out.println(weiXin.getInfo());
    }

    public void receiveQQ(QQ qq){
        System.out.println(qq.getInfo());
    }

    public void receiveDing(DingDing dingDing){
        System.out.println(dingDing.getInfo());
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:老师
 * @author: jie
 * @time: 2022/1/28 15:30
 */
public class Teacher {
    public void receiveWeiXin(WeiXin weiXin){
        System.out.println(weiXin.getInfo());
    }

    public void receiveQQ(QQ qq){
        System.out.println(qq.getInfo());
    }

    public void receiveDing(DingDing dingDing){
        System.out.println(dingDing.getInfo());
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 15:31
 */
public class Test {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.receiveQQ(new QQ("qq发送的消息"));
        teacher.receiveWeiXin(new WeiXin("微信发送的消息"));
        teacher.receiveDing(new DingDing("钉钉发送的消息"));
    }
}

从上面可以看出 调用方-高层模块(Teacher类)依赖低层模块(WinXin类、QQ类),这样造成的后果就是,当低层模块扩展,需要高层模块做出一定的调整;这就违反了开闭原则;

4.2 、使用后

package com.jie.principles.relyonreverse;
/**
 * @description:接口App
 * @author: jie 
 * @time: 2022/1/28 15:47
 */
interface App{
    String getInfo();
}
package com.jie.principles.relyonreverse;
/**
 * @description:老师
 * @author: jie
 * @time: 2022/1/28 15:30
 */
public class Teacher {
    public void receive(App app){
        System.out.println(app.getInfo());
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:微信
 * @author: jie
 * @time: 2022/1/28 15:31
 */
class WinXin implements App{
    private String info;

    public WinXin(String info) {
        this.info = info;
    }

    @Override
    public String getInfo() {
        return info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:QQ
 * @author: jie
 * @time: 2022/1/28 15:31
 */
class QQ implements App{
    private String info;

    public QQ(String info) {
        this.info = info;
    }

    @Override
    public String getInfo() {
        return info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 15:31
 */
public class Test {
    public static void main(String[] args) {
        Teacher teacher =new Teacher();
        teacher.receive(new WinXin("微信发来的消息"));
        teacher.receive(new QQ("qq发来的消息"));
    }
}

需求变更:老师新增了一个渠道“钉钉”获取学生的信息

package com.jie.principles.relyonreverse;
/**
 * @description:钉钉
 * @author: jie
 * @time: 2022/1/28 15:36
 */
class DingDing implements App {
    private String info;

    public DingDing(String info) {
        this.info = info;
    }

    @Override
    public String getInfo() {
        return info;
    }
}
package com.jie.principles.relyonreverse;
/**
 * @description:测试类
 * @author: jie
 * @time: 2022/1/28 15:31
 */
public class Test {
    public static void main(String[] args) {
        Teacher teacher =new Teacher();
        teacher.receive(new WinXin("微信发来的消息"));
        teacher.receive(new QQ("qq发来的消息"));
        teacher.receive(new DingDing("钉钉发来的消息"));
    }
}

从上面可以看出 调用方-高层模块(Teacher类)依赖低层模块对应的抽象(App接口),此时低层模块发生扩展,高层模块不受影响,这符合开闭原则;

5、接口隔离原则

概念: 客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。

案例:

需求:安全门具有防火、防水、防盗的功能。

5.1、使用前

package com.jie.principles.quarantine;
/**
 * @description:安全门功能接口
 * @author: jie
 * @time: 2022/1/28 16:23
 */
public interface SafetyDoor {
    /**
     * @description:防火
     * @author: jie
     * @time: 2022/1/28 16:27
     */
    void fireproofing();
    /**
     * @description:防水
     * @author: jie
     * @time: 2022/1/28 16:29
     */
    void waterproof();
    /**
     * @description:防盗
     * @author: jie
     * @time: 2022/1/28 16:30
     */
    void antiTheft();
}
package com.jie.principles.quarantine;
/**
 * @description:A品牌安全门
 * @author: jie
 * @time: 2022/1/28 16:32
 */
public class ASafetyGate implements SafetyDoor{
    @Override
    public void fireproofing() {
        System.out.println("防火");
    }

    @Override
    public void waterproof() {
        System.out.println("防水");
    }

    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }
}
package com.jie.principles.quarantine;
/**
 * @description:客户端
 * @author: jie
 * @time: 2022/1/28 16:41
 */
public class Client {
    public static void main(String[] args) {
        //A品牌安全门
        ASafetyGate aSafetyGate = new ASafetyGate();
        aSafetyGate.antiTheft();
        aSafetyGate.fireproofing();
        aSafetyGate.waterproof();
    }
}

需求变更:需要增加B品牌的安全门,且只需要防盗的功能。

package com.jie.principles.quarantine;
/**
 * @description:B品牌安全门
 * @author: jie
 * @time: 2022/1/28 16:32
 */
public class BSafetyGate implements SafetyDoor{
    @Override
    public void fireproofing() {
        System.out.println("防火");
    }

    @Override
    public void waterproof() {
        System.out.println("防水");
    }

    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }
}
package com.jie.principles.quarantine;

import com.jie.principles.lockage.BattleAcademy;

/**
 * @description:客户端
 * @author: jie
 * @time: 2022/1/28 16:41
 */
public class Client {
    public static void main(String[] args) {
        //A品牌安全门
        ASafetyGate aSafetyGate = new ASafetyGate();
        aSafetyGate.antiTheft();
        aSafetyGate.fireproofing();
        aSafetyGate.waterproof();
        System.out.println("_____________________________________________________");
        //B品牌安全门
        BSafetyGate bSafetyGate = new BSafetyGate();
        bSafetyGate.antiTheft();
        bSafetyGate.fireproofing();
        bSafetyGate.waterproof();
    }
}

从上面可以看出 BSafetyGate实现了SafetyDoor,但是BSafetyGate只需要防盗的功能。很显然违背了接口隔离原则。

5.2、使用后

package com.jie.principles.quarantine;
/**
 * @description:防火接口
 * @author: jie
 * @time: 2022/1/28 17:57
 */
public interface Fireproof {
    void fireproof();
}
package com.jie.principles.quarantine;
/**
 * @description:防水接口
 * @author: jie
 * @time: 2022/1/28 17:56
 */
public interface Waterproof {
    void waterproof();
}
package com.jie.principles.quarantine;
/**
 * @description:防盗接口
 * @author: jie
 * @time: 2022/1/28 17:57
 */
public interface AntiTheft {
    void antiTheft();
}
package com.jie.principles.quarantine;
/**
 * @description:A品牌安全门
 * @author: jie
 * @time: 2022/1/28 16:32
 */
public class ASafetyGate implements AntiTheft,Fireproof,Waterproof{
    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireproof() {
        System.out.println("防火");
    }


    @Override
    public void waterproof() {
        System.out.println("防水");
    }
}
package com.jie.principles.quarantine;
/**
 * @description:B品牌安全门
 * @author: jie
 * @time: 2022/1/28 16:32
 */
public class BSafetyGate implements AntiTheft{
    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }
}
package com.jie.principles.quarantine;

import com.jie.principles.lockage.BattleAcademy;

/**
 * @description:客户端
 * @author: jie
 * @time: 2022/1/28 16:41
 */
public class Client {
    public static void main(String[] args) {
        //A品牌安全门
        ASafetyGate aSafetyGate = new ASafetyGate();
        aSafetyGate.antiTheft();
        aSafetyGate.fireproof();
        aSafetyGate.waterproof();
        System.out.println("_____________________________________________________");
        //B品牌安全门
        BSafetyGate bSafetyGate = new BSafetyGate();
        bSafetyGate.antiTheft();
    }
}

6、迪米特法则

迪米特法则又叫最少知识原则。 只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。 其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。 迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。 其实就是单一职责原则的另一体现形式,将功能封装再封装,不要将所有的功能冗余在一起;

案例:

需求:明星由于全身心投入工作,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人。

package com.jie.principles.Dimit;
/**
 * @description:明星
 * @author: jie
 * @time: 2022/1/28 18:35
 */
public class Star {
    private String name;

    public Star(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
package com.jie.principles.Dimit;
/**
 * @description:粉丝
 * @author: jie
 * @time: 2022/1/28 18:35
 */
public class Fans {
    private String name;

    public Fans(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
package com.jie.principles.Dimit;
/**
 * @description:媒体公司
 * @author: jie
 * @time: 2022/1/28 18:36
 */
public class Company {
    private String name;

    public Company(String name) {
        this.name=name;
    }

    public String getName() {
        return name;
    }
}
package com.jie.principles.Dimit;

/**
 * @description:和媒体公司接口
 * @author: jie
 * @time: 2022/1/28 18:42
 */
public interface Business {
   /**
     * @description:和媒体公司洽谈的方法
     * @author: jie
     * @time: 2022/1/28 18:38
     */
    void business();

}
package com.jie.principles.Dimit;
/**
 * @description:和粉丝接口
 * @author: jie
 * @time: 2022/1/28 18:39
 */
public interface Meeting {
   /**
     * @description:和粉丝见面的方法
     * @author: jie
     * @time: 2022/1/28 18:38
     */
    void meeting();
}
package com.jie.principles.Dimit;
/**
 * @description:经纪人
 * @author: jie
 * @time: 2022/1/28 18:36
 */
public class Agent implements Meeting,Business{
    /**
     * @description:明星
     * @author: jie
     * @time: 2022/1/28 18:37
     */
    private Star star;
    /**
     * @description:粉丝
     * @author: jie
     * @time: 2022/1/28 18:37
     */
    private Fans fans;
    /**
     * @description:媒体公司
     * @author: jie
     * @time: 2022/1/28 18:37
     */
    private Company company;

    public void setStar(Star star) {
        this.star = star;
    }

    public void setFans(Fans fans) {
        this.fans = fans;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
  
    @Override
    public void meeting() {
        System.out.println(fans.getName() + "与明星" + star.getName() + "见面了。");
    }
   
    @Override
    public void business() {
        System.out.println(company.getName() + "与明星" + star.getName() + "洽淡业务。");
    }
}
package com.jie.principles.Dimit;

/**
 * @description:客户端
 * @author: jie
 * @time: 2022/1/28 18:47
 */
public class Client {

    public static void main(String[] args) {
        //1、创建经纪人类
        Agent agent = new Agent();
        //2、创建明星对象
        Star star = new Star("张三");
        agent.setStar(star);
        //创建粉丝对象
        Fans fans = new Fans("李四");
        agent.setFans(fans);
        //创建媒体公司对象
        Company company = new Company("英杰媒体公司");
        agent.setCompany(company);

        //和粉丝
        agent.meeting();
        //和媒体公司
        agent.business();
    }

}

7、合成复用原则

合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。 通常类的复用分为继承复用和合成复用两种。 继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
  3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:

  1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。
  2. 对象间的耦合度低。可以在类的成员位置声明抽象。
  3. 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

8、本篇博客所写代码

软件设计原则: 软件设计原则案例所敲代码