您的位置:首页 > 运维架构 > 网站架构

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

2020-02-06 04:24 513 查看

这里写自定义目录标题

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

    软件架构设计原则

    开闭原则

    开闭原则(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. 增加了系统的抽象和理解难度。
    • 点赞
    • 收藏
    • 分享
    • 文章举报
    微笑面对所有 发布了4 篇原创文章 · 获赞 3 · 访问量 1060 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: