设计模式——概述、UML图、原则(二)

简介: 设计模式——概述、UML图、原则

3.3 依赖倒转原则


高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。


下面看一个例子来理解依赖倒转原则


【例】组装电脑


现要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,海盗船等。


类图如下:

1.png

代码如下:


希捷硬盘类(XiJieHardDisk):

public class XiJieHardDisk implements HardDisk {
    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据" + data);
    }
    public String get() {
        System.out.println("使用希捷希捷硬盘取数据");
        return "数据";
    }
}

Intel处理器(IntelCpu):

public class IntelCpu implements Cpu {
    public void run() {
        System.out.println("使用Intel处理器");
    }
}

金士顿内存条(KingstonMemory):

public class KingstonMemory implements Memory {
    public void save() {
        System.out.println("使用金士顿作为内存条");
    }
}

电脑(Computer):

public class Computer {
    private XiJieHardDisk hardDisk;
    private IntelCpu cpu;
    private KingstonMemory memory;
    public IntelCpu getCpu() {
        return cpu;
    }
    public void setCpu(IntelCpu cpu) {
        this.cpu = cpu;
    }
    public KingstonMemory getMemory() {
        return memory;
    }
    public void setMemory(KingstonMemory memory) {
        this.memory = memory;
    }
    public XiJieHardDisk getHardDisk() {
        return hardDisk;
    }
    public void setHardDisk(XiJieHardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }
    public void run() {
        System.out.println("计算机工作");
        cpu.run();
        memory.save();
        String data = hardDisk.get();
        System.out.println("从硬盘中获取的数据为:" + data);
    }
}

测试类(TestComputer):


测试类用来组装电脑。

public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setHardDisk(new XiJieHardDisk());
        computer.setCpu(new IntelCpu());
        computer.setMemory(new KingstonMemory());
        computer.run();
    }
}

上面代码可以看到已经组装了一台电脑,但是似乎组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,这对用户肯定是不友好的,用户有了机箱肯定是想按照自己的喜好,选择自己喜欢的配件。


根据依赖倒转原则进行改进:


代码我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类。


类图如下:

1.png

电脑(Computer):

public class Computer {
    private HardDisk hardDisk;
    private Cpu cpu;
    private Memory memory;
    public HardDisk getHardDisk() {
        return hardDisk;
    }
    public void setHardDisk(HardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }
    public Cpu getCpu() {
        return cpu;
    }
    public void setCpu(Cpu cpu) {
        this.cpu = cpu;
    }
    public Memory getMemory() {
        return memory;
    }
    public void setMemory(Memory memory) {
        this.memory = memory;
    }
    public void run() {
        System.out.println("计算机工作");
    }
}

面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。


3.4 接口隔离原则


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


下面看一个例子来理解接口隔离原则


【例】安全门案例


我们需要创建一个黑马品牌的安全门,该安全门具有防火、防水、防盗的功能。可以将防火,防水,防盗功能提取成一个接口,形成一套规范。类图如下:

1.png

上面的设计我们发现了它存在的问题,黑马品牌的安全门具有防盗,防水,防火的功能。现在如果我们还需要再创建一个传智品牌的安全门,而该安全门只具有防盗、防水功能呢?很显然如果实现SafetyDoor接口就违背了接口隔离原则,那么我们如何进行修改呢?看如下类图:

1.png

代码如下:


AntiTheft(接口):

public interface AntiTheft {
    void antiTheft();
}

Fireproof(接口):

public interface Fireproof {
    void fireproof();
}

Waterproof(接口):

public interface Waterproof {
    void waterproof();
}

HeiMaSafetyDoor(类):

public class HeiMaSafetyDoor implements AntiTheft,Fireproof,Waterproof {
    public void antiTheft() {
        System.out.println("防盗");
    }
    public void fireproof() {
        System.out.println("防火");
    }
    public void waterproof() {
        System.out.println("防水");
    }
}

ItcastSafetyDoor(类):

public class ItcastSafetyDoor implements AntiTheft,Fireproof {
    public void antiTheft() {
        System.out.println("防盗");
    }
    public void fireproof() {
        System.out.println("防火");
    }
}


3.5 迪米特法则


迪米特法则又叫最少知识原则。


只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。


其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。


迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。


下面看一个例子来理解迪米特法则


【例】明星与经纪人的关系实例


明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。


类图如下:

1.png

代码如下:


明星类(Star)

public class Star {
    private String name;
    public Star(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
}

粉丝类(Fans)

public class Fans {
    private String name;
    public Fans(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
}

媒体公司类(Company)

public class Company {
    private String name;
    public Company(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
}

经纪人类(Agent)

public class Agent {
    private Star star;
    private Fans fans;
    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;
    }
    public void meeting() {
        System.out.println(fans.getName() + "与明星" + star.getName() + "见面了。");
    }
    public void business() {
        System.out.println(company.getName() + "与明星" + star.getName() + "洽淡业务。");
    }
}


3.6 合成复用原则


合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。


通常类的复用分为继承复用和合成复用两种。


继承复用虽然有简单和易实现的优点,但它也存在以下缺点:


继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。

子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

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


它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

对象间的耦合度低。可以在类的成员位置声明抽象。

复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

下面看一个例子来理解合成复用原则


【例】汽车分类管理程序


汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。类图如下:

1.png

从上面类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类。我们试着将继承复用改为聚合复用看一下。

1.png


相关文章
|
4天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
4天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
4天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
4天前
|
设计模式 Java 程序员
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
|
11天前
|
设计模式 算法 搜索推荐
设计模式概述
设计模式是软件工程中的最佳实践,帮助开发者解决常见问题,提高代码的可重用性、可读性和可靠性。学习设计模式可以提升思维能力、标准化程序设计、增强代码质量。文章介绍了设计模式的分类(创建型、结构型、行为型)及其在流行框架中的应用,建议读者掌握SOLID原则并深入学习GoF的23种设计模式。
58 3
设计模式概述
|
3月前
|
设计模式 Java 测试技术
Java设计模式-UML与设计原则(1)
Java设计模式-UML与设计原则(1)
|
4月前
|
设计模式 uml
设计模式常用的UML图------类图
这篇文章介绍了UML中类图的基本概念和用途,详细解释了类与接口、类之间的关系,包括继承、实现、组合、聚合、关联和依赖等六种关系,并展示了它们在类图中的表示方法。
设计模式常用的UML图------类图
|
5月前
|
设计模式 Java 开发者
设计模式概述
设计模式概述
|
6月前
|
设计模式 算法 Java
Java设计模式:核心概述(一)
Java设计模式:核心概述(一)
|
6月前
|
设计模式 uml
设计模式学习心得之前置知识 UML图看法与六大原则(下)
设计模式学习心得之前置知识 UML图看法与六大原则(下)
49 2