软件架构设计原则和工厂模式

本文深入探讨软件架构设计的七大原则,包括开闭、依赖倒置、单一职责、接口隔离、迪米特法则、里氏替换及合成复用原则,并详解工厂模式的三种形式:简单工厂、工厂方法和抽象工厂模式,旨在提升代码质量和系统灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

软件架构设计原则和工厂模式

软件架构设计原则

开闭原则

开闭原则(Open-Closed Principle, OCP)是指一个软件实体,如类、模块和函数应该对扩展开放,对修改关闭。

依赖倒置原则

依赖倒置原则(Dependence Inversion Principle, DIP)是指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象。抽象不应该依赖细节;细节应该依赖抽象。通过依赖倒置,可以减少类与类之间的耦合性,提高系统稳定性,提高代码的可读性和可维护性,并能降低修改程序所造成的风险。

单一职责原则

单一职责(Simple Responsibility Principle, SRP)是指不要存在多于一个导致类变更的原因。假设我们有一个Class负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能导致另一个发生故障。

接口隔离原则

接口隔离原则(Interface Segregation Principle,ISP)是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。这个原则指导我们在设计接口的时候应该注意以下几点

  1. 一个类对一类的依赖应该建立在最小的接口之上。
  2. 建立单一接口,不要建立庞大臃肿的接口
  3. 尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)

接口隔离原则符合我们常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性。

迪米特法则

迪米特原则(Law of Demeter,LOD)是指一个对象应该对其他对象保持最少的了解,又叫最少知道原则(Least Knowledge Principle,LKP),尽量降低类与类之间的耦合。迪米特原则主要强调只和朋友交流,不和陌生人说话。

里氏替换原则

里氏替换原则(Liskov Substitution Principle,LSP)是指如果对每一个类型为T1的对象o1,都有类型T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

可以理解为一个软件实体如果使用一个父类的话,那一定是适用于其子类,所有引用父类的地方必须能透明的使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。根据这个理解,我们总结一下:子类可以扩展分类的功能,但不能改变父类的功能。

  1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  2. 子类中可以增加自己特有的方法。
  3. 当子类的方法重载父类的方法时,方法的前置条件(即方法的入参)要比父类方法的输入参数更宽松。
  4. 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出)要比父类更严格或相等。

合成复用原则

合成复用原则(Composite/Aggregate Reuse Principle,CARP)是指尽量使用对象组合(has-a)/聚合(contains-a),而不是继承关系达到软件复用的目的。可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

总结:学习设计原则,是学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、世间、成本、质量,不是可以追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。

工厂模式

简单工厂模式

这个没什么说的,直接上代码…

public interface BMW {
    void run();
}

public class BMW320 implements BMW {
    @Override
    public void run() {
        System.out.println("BMW320 running");
    }
}`

public class BMW520 implements BMW {

   @Override
   public void run() {
       System.out.println("BMW520 running");
   }
}

public class BMWFactory {

   public static BMW createBMW(String type) {
       if("bmw320".equals(type)) {
           return new BMW320();
       } else if("bmw520".equals(type)) {
           return new BMW520();
       } else {
           throw new RuntimeException("没有这个类型");
       }
   }
}

public static void main(String[] args) {
   BMW bmw320 = BMWFactory.createBMW("bmw320");
   bmw320.run();

   BMW bmw520 = BMWFactory.createBMW("bmw520");
   bmw520.run();

}

简单工厂类图

首先,我们创建了宝马系列的产品,然后由宝马工厂生产你需要的产品。获取的方式是,你先告诉宝马产商,你需要宝马的那个产品,然后工厂会创建一个你需要的产品。
这种设计模式的缺点是:不符合开闭原则,每当新增一个产品,我们都需要去修改现有的代码,不利于扩展。
优点:只需传入一个参数,就可以获取你需要的对象,不需要知道对象创建的细节。

我们可以通过修改入参,来改善这个问题。
1.把入参改成包名+类名,然后通过反射来创建对象(需要强转)
2.直接传入一个class 对象,通过反射创建对象

简单工厂模式在JDK源码中的应用,例如Calendar.getInstance(),进入getInstance()方法后我们可以看到内部实现用的其实也是简单工厂

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

工厂方法模式

工厂方法模式(Factory Method Pattern)是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化那个类,工厂方法让类的实例化推迟到子类中进行。

把上面的代码修改下:

public interface IFactory {
    BMW createBMW();
}

public class BMW320Factory implements IFactory {
    @Override
    public BMW createBMW() {
        return new BMW320();
    }
}
public class BMW520Factory implements IFactory {
    @Override
    public BMW createBMW() {
        return new BMW520();
    }
}

测试代码:

public class Test {

    public static void main(String[] args) {
        IFactory bmw320Factory = new BMW320Factory();
        BMW bmw320 = bmw320Factory.createBMW();
        bmw320.run();

        IFactory bmw520Factory = new BMW320Factory();
        BMW bmw520 = bmw520Factory.createBMW();
        bmw520.run();
    }
}

类图:
工厂方法模式
Logback中的工厂模式就是使用的这种,这里就不细说了。

工厂方法适用于以下场景:

  1. 创建对象需要大量的重复代码
  2. 客户端不依赖于产品类实例如何被创建、实现等细节
  3. 一个类通过其子类来指定创建哪个对象

优点:符合开闭原则,易于扩展
缺点:类的个数容易过多,增加复杂度。增加了系统的抽象性和理解难度

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。在讲解抽象工厂之前,我们需要了解两个概念,产品等级结构和产品族,看下面的图:
在这里插入图片描述
从上图中看出有正方形,圆形和菱形三种图形,相同颜色深浅的就代表同一个产品族,相同形状的代表同一个产品等级结构。同样可以从生活中来举例,比如,美的电器生产多种家用电器。那么上图中,颜色最深的正方形就代表美的洗衣机、颜色最深的圆形代表美的空调、颜色最深的菱形代表美的热水器,颜色最深的一排都属于美的品牌,都是美的电器这个产品族。再看最右侧的菱形,颜色最深的我们指定了代表美的热水器,那么第二排颜色稍微浅一点的菱形,代表海信的热水器。同理,同一产品结构下还有格力热水器,格力空调,格力洗衣机。

接下来我们看下代码实现:

/**
 * 空调
 */
public interface AirConditioning {
    void refrigeration();
}
/**
 * 洗衣机
 */
public interface WashingMachine {
    void wash();
}
/**
 * 热水器
 */
public interface WaterHeater {
    void boilWater();
}
public class HisenseAirConditioning implements AirConditioning {
    @Override
    public void refrigeration() {
        System.out.println("海信空调制冷");
    }
}

public class HisenseWashingMachine implements WashingMachine {

    @Override
    public void wash() {
        System.out.println("海信洗衣机洗衣");
    }
}
public class HisenseWaterHeater implements WaterHeater {

    @Override
    public void boilWater() {
        System.out.println("海信热水器热水");
    }
}

public class MideaAirConditioning implements AirConditioning {
    @Override
    public void refrigeration() {
        System.out.println("美的空调制冷");
    }
}

public class MideaWashingMachine implements WashingMachine {
    @Override
    public void wash() {
        System.out.println("美的洗衣机洗衣服");
    }
}
public class MideaWaterHeater implements WaterHeater {

    @Override
    public void boilWater() {
        System.out.println("美的热水器热水");
    }
}
/**
 * 电器工厂
 */
public interface ElectricalApplianceFactory {

    /**
     * 创建热水器
     * @return
     */
    WaterHeater createWaterHeater();

    /**
     * 创建空调
     * @return
     */
    AirConditioning createAirConditioning();

    /**
     * 创建洗衣机
     * @return
     */
    WashingMachine createWashingMachine();
}
public class HisenseFactory implements ElectricalApplianceFactory {
    @Override
    public WaterHeater createWaterHeater() {
        return new HisenseWaterHeater();
    }

    @Override
    public AirConditioning createAirConditioning() {
        return new HisenseAirConditioning();
    }

    @Override
    public WashingMachine createWashingMachine() {
        return new HisenseWashingMachine();
    }
}
public class HisenseFactory implements ElectricalApplianceFactory {
    @Override
    public WaterHeater createWaterHeater() {
        return new HisenseWaterHeater();
    }

    @Override
    public AirConditioning createAirConditioning() {
        return new HisenseAirConditioning();
    }

    @Override
    public WashingMachine createWashingMachine() {
        return new HisenseWashingMachine();
    }
}
public class Test {

    public static void main(String[] args) {
        ElectricalApplianceFactory electricalApplianceFactory = new HisenseFactory();
        AirConditioning airConditioning = electricalApplianceFactory.createAirConditioning();
        airConditioning.refrigeration();
        WashingMachine washingMachine = electricalApplianceFactory.createWashingMachine();
        washingMachine.wash();
        WaterHeater waterHeater = electricalApplianceFactory.createWaterHeater();
        waterHeater.boilWater();

        electricalApplianceFactory = new MideaFactory();
        WaterHeater waterHeater1 = electricalApplianceFactory.createWaterHeater();
        waterHeater1.boilWater();
        WashingMachine washingMachine1 = electricalApplianceFactory.createWashingMachine();
        washingMachine1.wash();
        AirConditioning airConditioning1 = electricalApplianceFactory.createAirConditioning();
        airConditioning1.refrigeration();
    }
}

上面代码描述了三个产品族热水器、空调和洗衣机,也描述了两个产品等级美的和海信。抽象工厂可以清晰的描述这样的一层复杂的关系。但是如果后期还要加产品,比如说冰箱,那么我们从抽象工厂到具体的工厂全部都要调整,不符合开闭原则。所以它也是有缺点的:

  1. 扩展困难,需要修改抽象工厂的接口
  2. 增加了系统的抽象和理解难度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值