规划形式概述

规划形式的背景

  • 规划面向方针软件比较困难,而规划能够复用的面向方针软件更加困难
  • 不是处理任何问题都需求从头做起,最好能复用以往的规划计划经历
  • 面向方针软件规划经历需求有必定的形式记载下来,以供给给其他规划者运用,这就产生了规划形式。

什么是规划形式

规划形式 (Design pattern)是一套被反复运用的、经过分类编目的面向方针程序规划经历总结。它是面向方针程序规划中典型问题的处理计划

GoF形式是四位闻名面向方针规划专家 (Elich Gamma、Richard Helm、Ralph Johnson、 John Vlissides) 所提出的23种面向方针程序规划形式。这四个人常被称为Gang of Four,简称GoF。

规划形式作用

  • 协助规划者更快、更好完结面向方针程序规划。
  • 进步完结代码编写的规范性,标准化开发软件构件。
  • 支撑编写可扩展、易保护、好办理的程序代码。
  • 让代码更简单被他人了解,确保代码可靠性、重用性。

规划形式类型

序号 形式类型 说明
1 发明形式 与方针创立有关,供给一种创立方针而躲藏创立逻辑,给出灵活创立方针的处理计划。典型形式有5种。
2 结构形式 处理类或方针的组合,给出运用承继、接口组合方针以获得新功用的处理计划。典型形式有7种。
3 行为形式 用于描绘方针之间协作完结特定功用及其职责分配,给出方针之间通讯的处理计划。典型形式有11种。

创立型形式

创立型形式的关注点是“怎样创立方针?”它的首要特点是“将方针的创立与运用别离”,然后下降方针之间的耦合度。

一、创立型形式类型

  • 单例形式 (Singleton Pattern)
  • 工厂形式 (Factory Pattern)
  • 笼统工厂形式 (Abstract Factory Pattern)
  • 建造者形式 (Builder Pattern)
  • 原型形式 (Prototype Pattern)

单例形式 (Singleton Pattern)

单例形式是指一个类只能有一个实例,且该类能自行创立这个实例的一种程序规划形式。

形式动机

虽然软件体系能够创立多个进程任务并发运转,但体系在同一时段只允许一个进程任务运转处理。如下一些计算机软件程序需求选用单一进程处理。

  • 打印机程序
  • Windows 的回收站
  • 操作体系中的文件体系
  • 多线程中的线程池、数据库的衔接池
  • 运用程序的对话框
  • Web运用的装备方针

形式处理问题

怎么让体系只允许仅有的实例进行服务资源拜访,然后处理多实例形式带来的体系开支问题。

处理问题思想:

  • 单例形式类只允许创立一个实例方针;
  • 该实例方针必须在单例形式类初始化时自行创立;
  • 单例形式类对外仅供给一个拜访该单例的大局拜访点。

形式规划计划

  • 选用一个类仅能创立一个实例方针。
  • 该类需自行创立这个实例方针。
  • 该类供给大局揭露办法让外部来获取这个实例方针。

电子科大软件体系架构规划——规划形式

形式完结计划

饿汉式单例类:在单例类被初始化加载时,就实例化一个方针交给自己的引证。

// 饿汉式单例类
public class Singleton {
    private static Singleton singletonVar = new Singleton();//在界说变量时就创立方针
    private Singleton() {
    } //私有的结构办法
    public static Singleton getInstance() { //静态公有办法回来创立的方针
        return singletonVar;
    }
}

懒汉式单例类:在拜访实例方针时才实例化方针。

public class Singleton {
    private static Singleton singletonVar; //私有的静态方针变量
    private Singleton() {
    } //私有的结构办法
    public static synchronized Singleton getInstance() { //拜访实例方针时创立单例类实例
        if (singletonVar == null) {
            singletonVar = new Singleton();
        }
        return singletonVar;
    }
}

synchronized关键字的作用是完结线程同步,确保在同一时间只需一个线程能够拜访某个资源。在懒汉式单例类中,getInstance()办法是获取单例方针的仅有进口,因而运用synchronized关键字能够确保在多线程环境下,只需一个线程能够创立单例方针,然后确保单例方针的仅有性。假如不运用synchronized关键字,在多线程环境下,或许会有多个线程一同履行getInstance()办法,然后导致多个单例方针被创立。这将会破坏单例形式的规划意图。

适用场景

  1. 需求频频实例化方针,然后毁掉方针。
  2. 创立方针时开支较大,但又需常常运用该方针。
  3. 有状况的东西类方针。
  4. 频频拜访的数据库或文件方针。

运用示例——用懒汉式单例形式模拟产生美国当今总统方针

电子科大软件体系架构规划——规划形式

  • 规划一个 President单例形式类。该类供给了一个静态办法,供外界获取它的静态实例。
  • SingletonLazy类拜访 President单例类来获取 President方针。

SingletonLazy类编程代码:

public class SingletonLazy {
    public static void main(String[] args) {
        President zt1 = President.getInstance();
        zt1.getName();  // 输出总统的名字
        President zt2 = President.getInstance();
        zt2.getName();  // 输出总统的名字
        if (zt1 == zt2) {
            System.out.println("他们是同一人!");
        } else {
            System.out.println("他们不是同一人!");
        }
    }
}

President类编程代码:

class President {
    private static volatile President instance = null;  //确保 instance 在一切线程中同步
    //private防止类在外部被实例化
    private President() {
        System.out.println("产生一个总统!");
    }
    public static synchronized President getInstance() {
        //在getInstance办法上加静态锁,防止并发线程拜访产生多个实例
        if (instance == null) {
            instance = new President();
        } else {
            System.out.println("已经有一个总统,不能产生新总统!");
        }
        return instance;
    }
    public void getName() {
        System.out.println("我是美国总统:拜登。");
    }
}

完好代码:

public class SingletonLazy {
    public static void main(String[] args) {
        President zt1 = President.getInstance();
        zt1.getName();  // 输出总统的名字
        President zt2 = President.getInstance();
        zt2.getName();  // 输出总统的名字
        if (zt1 == zt2) {
            System.out.println("他们是同一人!");
        } else {
            System.out.println("他们不是同一人!");
        }
    }
}
class President {
    private static volatile President instance = null;  //确保 instance 在一切线程中同步
    //private防止类在外部被实例化
    private President() {
        System.out.println("产生一个总统!");
    }
    public static synchronized President getInstance() {
        //在getInstance办法上加静态锁,防止并发线程拜访产生多个实例
        if (instance == null) {
            instance = new President();
        } else {
            System.out.println("已经有一个总统,不能产生新总统!");
        }
        return instance;
    }
    public void getName() {
        System.out.println("我是美国总统:拜登。");
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

形式优缺陷

长处:

  • 在内存中一个类只需一个实例,可减少程序占用内存的开支。
  • 防止频频的创立和毁掉实例,能够进步软件程序运转性能。
  • 防止对资源的多重占用。
  • 供给大局拜访点,能够共享资源拜访。

缺陷:

  • 单例形式一般没有接口,扩展困难。假如要扩展功用,需修正本来的代码,这违反开闭准则。
  • 在并发编程中,单例形式不利于代码调试,因在单例中的代码没有履行完,不能模拟生成一个新的方针。
  • 单例形式的功用代码通常写在一个类中。假如功用规划不合理,则很简单违反单一职责准则。

针对如下民政服务体系的婚姻登记功用规划类图,怎么选用饿汉式单例形式完结Java程序编写,并在主程序中输出音讯反响。

电子科大软件体系架构规划——规划形式

class MarriageRegister {
    // 私有静态实例,在类加载时创立
    private static final MarriageRegister instance = new MarriageRegister();
    // 私有结构办法,防止外部经过new创立多个实例
    private MarriageRegister() {
        // 初始化操作
    }
    // 公有静态办法,回来仅有实例
    public static MarriageRegister getInstance() {
        return instance;
    }
    // 公有办法,输出音讯
    public void showMessage() {
        System.out.println("Marriage registration is successful.");
    }
}
public class Client {
    public static void main(String[] args) {
        // 获取MarriageRegister的单例实例
        MarriageRegister register = MarriageRegister.getInstance();
        // 输出音讯
        register.showMessage();
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

结构型形式

结构型形式描绘怎么将类或方针组成更大的结构完结特定功用特性。它分为类结构型形式和方针结构型形式。前者选用承继机制来安排接口和类,后者选用组合或聚合来安排方针。

结构型形式类型

  • 适配器形式 (Adapter Pattern)
  • 桥接形式 (Bridge Pattern)
  • 装修器形式 (Decorator Pattern)
  • 外观形式 (Facade Pattern)
  • 组合形式 (Composite Pattern)
  • 享元形式 (Flyweight Pattern)
  • 署理形式 (Proxy Pattern)

适配器形式 (Adapter Pattern)

适配器形式完结不同类接口之间的转化,使接口不兼容的那些类也能一同工作。

形式动机

当运用现有的类库完结体系的新功用开发时,发现当时体系的接口与现有类库规范不兼容。假如从头开发这些类本钱又很高,这时能够运用适配器形式来处理现有类的复用问题。

处理首要问题

  • 体系需求运用现有的类,但该类的接口不契合体系的拜访需求。

  • 怎么经过接口转化,使一个类能够拜访另一个类。

形式规划计划

电子科大软件体系架构规划——规划形式

  • 方针接口 (Target) : 当时体系业务所期待运用的接口。
  • 适配者类 (Adaptee):它是被拜访和适配的现有组件类.
  • 适配器类 (classAdapter ) : 它是个转化器,经过承继适配者和完结方针接口,让客户端类可经过方针接口拜访适配者。

形式处理计划

电子科大软件体系架构规划——规划形式

package adapter;
//方针接口
interface Target {
    public void request();
}
//被适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("适配者中的业务代码被调用!");
    }
}
// 适配器类
class ClassAdapter extends Adaptee implements Target {
    public void request() {
        specificRequest();
    }
}
// 客户端测验代码
public class ClassAdapterTest {
    public static void main(String[] args) {
        System.out.println("类适配器形式测验:");
        Target target = new ClassAdapter();
        target.request();
    }
}

运转输出成果:

电子科大软件体系架构规划——规划形式

适用场景

  • 不想修正原有代码而重用现有的类功用。
  • 将一个类的接口转化成客户期望的另外一个接口,使得原本不兼容的那些类|能一同工作。
  • 运用第三方供给的类,但类接口界说和自己要求的接口界说不同。

运用示例

电子科大软件体系架构规划——规划形式

  • 为了完结AudioPlayer播映其他格局的音频文件。需求创立一个完结 MediaPlayer接口的适配器类MediaAdapter。该适配器类运用AdvancedMediaPlayer类的方针来播映所需的媒体文件格局。

  • Audioplayer 运用适配器类MediaAdapter 传递所需的音频类型,它不需求知道能播映所需格局音频的实际类。

  • AdapterPatternDemo 类运用AudioPlayer 类和适配器类MediaAdapter 来播映各种格局文件。

MediaPlayer接口:

package AdapterPattern;
//媒体播映器接口
public interface MediaPlayer {
    public void play(String audioType, String fileName);
}

AdvancedMediaPlayer接口:

package AdapterPattern;
//高级媒体播映器接口
public interface AdvancedMediaPlayer {
    public void playVlc(String fileName);
    public void playMp4(String fileName);
}

VlcPlayer类:

package AdapterPattern;
//创立完结了 AdvancedMediaPlayer 接口的实体类
public class VlcPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("播映 vlc file. Name: "   fileName);
    }
    @Override
    public void playMp4(String fileName) {
        //什么也不做
    }
}

Mp4Player类:

package AdapterPattern;
//创立完结了 AdvancedMediaPlayer 接口的实体类
public class Mp4Player implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        //什么也不做
    }
    @Override
    public void playMp4(String fileName) {
        System.out.println("播映 mp4 file. Name: "   fileName);
    }
}

MediaAdapter类:

package AdapterPattern;
public class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;
    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new Mp4Player();
        }
    }
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

AudioPlayer类:

package AdapterPattern;
public class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;
    @Override
    public void play(String audioType, String fileName) {
        // 播映 mp3 音乐文件的内置支撑
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing mp3 file. Name: "   fileName);
        }
        // mediaAdapter 供给了播映其他文件格局的支撑
        else if (audioType.equalsIgnoreCase("vlc")
                || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. "  
                    audioType   " format not supported");
        }
    }
}

AdapterPatternDemo类:

package AdapterPattern;
//运用AudioPlayer来播映不同类型的音频格局
public class AdapterPatternDemo {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

补:equalsIgnoreCase 是 Java 中的一个办法,用于比较两个字符串是否相等,而疏忽它们的大小写。这个办法是 String 类的成员,所以能够对任何字符串方针调用它。

形式优缺陷

长处:

  • 客户端经过适配器能够透明地调用方针接口。
  • 程序员不需求修正原有代码而重用现有的适配者类。
  • 将方针类和适配者类解耦,处理了方针类和适配者类接口不一致的问题。
  • 该形式契合开闭准则。

缺陷:

  • 适配器编写过程需求结合业务场景全面考虑,或许会增加编程的杂乱性。
  • 增加代码阅读难度,下降代码可读性,过多运用适配器会使体系代码变得杂乱。

剖析下面规划类图怎么处理新能源轿车的发动机接口?给出Java程序完结该规划功用。

// Motor接口
interface Motor {
    void drive();
}
// 电动电机类
class ElectricMotor {
    public void electricDrive() {
        System.out.println("Electric Motor drive.");
    }
}
// 光电电机类
class OpticalMotor {
    public void opticalDrive() {
        System.out.println("Optical Motor drive.");
    }
}
// 电动电机适配器
class ElectricAdapter implements Motor {
    private ElectricMotor emotor;
    public ElectricAdapter() {
        emotor = new ElectricMotor();
    }
    public void drive() {
        emotor.electricDrive();
    }
}
// 光电电机适配器
class OpticalAdapter implements Motor {
    private OpticalMotor omotor;
    public OpticalAdapter() {
        omotor = new OpticalMotor();
    }
    public void drive() {
        omotor.opticalDrive();
    }
}
// 读取XML装备文件
class ReadXML {
    // 这儿供给一个示例办法,用于模拟从XML装备中获取电机方针
    public static Object getObject() {
        // 这儿能够依据实际情况读取XML装备,实例化不同的Motor
        // 以下代码仅作为示例
        return new ElectricAdapter(); // 或许 return new OpticalAdapter();
    }
}
public class MotorAdapterTest {
    public static void main(String[] args) {
        // 假设ReadXML.getObject()办法回来一个Motor类型的方针
        Motor motor = (Motor) ReadXML.getObject();
        motor.drive();
    }
}

运转成果:

桥接形式 (Bridge Pattern)

桥接形式是一种用于把笼统类与完结类解耦,使得二者都能够完结独立改动的规划形式。它经过在笼统类和完结类之间加入桥接接口,来完结二者的解耦。

形式动机

在一些运用中,某些类具有多个维度的改动,如既可按形状扩展,又可按色彩扩展。假如类选用承继办法完结m种形状和n种色彩进行扩展建模,其子类就有 mn 种,其扩展较困难。当选用桥接形式能够将笼统类与完结类解耦,使得二者能够独立改动,然后简单完结功用类扩展。

处理问题

  • 怎么将笼统类与完结类别离,使它们都能够独登时进行改动。
  • 在笼统类与完结类都有多种改动情况下,怎么处理扩展困难问题。

形式规划计划

电子科大软件体系架构规划——规划形式

  • 笼统化人物类 (Abstraction) 保护一个指向Implementor接口的指针。
  • 扩展笼统化人物类(RefinedAbstraction )扩大笼统人物类Abstraction功用。
  • 接口类 (Implementor)界说完结类的接口。
  • 完结类 (ConcretelmpIementor )详细界说完结操作。

形式完结计划

package bridge;
public class BridgeTest {
    public static void main(String[] args) {
        Implementor imple = new ConcreteImplementorA();
        Abstraction abs = new RefinedAbstraction(imple);
        abs.Operation();
    }
}
//完结化接口
interface Implementor {
    public void OperationImpl();
}
//详细完结化类
class ConcreteImplementorA implements Implementor {
    public void OperationImpl() {
        System.out.println("详细完结化(Concrete Implementor)人物被拜访");
    }
}
//笼统化人物
abstract class Abstraction {
    protected Implementor imple;
    protected Abstraction(Implementor imple) {
        this.imple = imple;
    }
    public abstract void Operation();
}
//扩大笼统化人物
class RefinedAbstraction extends Abstraction {
    protected RefinedAbstraction(Implementor imple) {
        super(imple);
    }
    public void Operation() {
        System.out.println("扩大笼统化(Refined Abstraction)人物被拜访");
        imple.OperationImpl();
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

适用场景

  • 当一个类存在两个独立改动的维度,且这两个维度都需求进行扩展。
  • 当一个体系不期望运用承继或多层次承继导致子类个数急剧增加。
  • 当一个体系需求在构件的笼统化类和详细化完结类之间增加更多的灵活性。

运用示例

电子科大软件体系架构规划——规划形式

  • 完结接口 DrawAPI及其实体类RedCircle、GreenCircle。
  • Shape 笼统类将运用DrawAPI 接口方针。Circle为扩展笼统类。
  • BridgePatternDemo 类 运用 Shape类来画出不同色彩的圆。
package BridgePattern;
//画图API接口
interface DrawAPI {
    public void drawCircle(int radius, int x, int y);
}
//红色圆圈的详细完结
class RedCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: red, radius: "   radius   ", x: "   x   ", y: "   y   "]");
    }
}
//绿色圆圈的详细完结
class GreenCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: green, radius: "   radius   ", x: "   x   ", y: "   y   "]");
    }
}
//笼统的形状类
abstract class Shape {
    protected DrawAPI drawAPI;
    protected Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }
    public abstract void draw();
}
//圆形类的完结
class Circle extends Shape {
    private int x, y, radius;
    public Circle(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    public void draw() {
        drawAPI.drawCircle(radius, x, y);
    }
}
//桥接形式的演示类
public class BridgePatternDemo {
    public static void main(String[] args) {
        Shape redCircle = new Circle(100, 100, 10, new RedCircle());
        Shape greenCircle = new Circle(100, 100, 10, new GreenCircle());
        redCircle.draw();
        greenCircle.draw();
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

形式优缺陷

长处:

  • 笼统类和完结类的别离有助于下降对完结部分编译时的依靠
  • 可独登时对笼统类和完结类的层次结构进行扩大
  • 完结细节对客户透明
  • 契合开闭准则

缺陷:

  • 桥接形式会增加体系的了解与规划难度
  • 要求开发者针对笼统类进行规划与编程

剖析如下女士皮包选购功用类图规划怎么运用桥接形式?怎么编写Java程序完结该功用。

电子科大软件体系架构规划——规划形式

// Color接口及其详细完结
interface Color {
    String getColor();
}
class Yellow implements Color {
    public String getColor() {
        return "yellow";
    }
}
class Red implements Color {
    public String getColor() {
        return "red";
    }
}
// Bag笼统类及其详细完结
abstract class Bag {
    protected Color color;
    public void setColor(Color color) {
        this.color = color;
    }
    public abstract String getName();
}
class HandBag extends Bag {
    public String getName() {
        return "HandBag";
    }
}
class Wallet extends Bag {
    public String getName() {
        return "Wallet";
    }
}
// BagManage类来展示怎么运用桥接形式
public class BagManage {
    public static void main(String[] args) {
        // 创立详细完结化人物
        Color yellow = new Yellow();
        Color red = new Red();
        // 创立扩大笼统化人物,并桥接详细完结化人物
        Bag handBag = new HandBag();
        handBag.setColor(yellow); // 设置黄色
        Bag wallet = new Wallet();
        wallet.setColor(red); // 设置红色
        // 操作
        System.out.println("The "   handBag.getName()   " is "   handBag.color.getColor());
        System.out.println("The "   wallet.getName()   " is "   wallet.color.getColor());
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

行为型形式

行为型形式用于处理程序在运转时存在的杂乱流程操控,如多个方针之间彼此协作完结功用任务,以及怎么分配职责。

行为型形式类型

  • 职责链形式 (Chain of Responsibility Pattern)
  • 命令形式 (Command Pattern)
  • 解释器形式 (Interpreter Pattern)
  • 迭代器形式 (lterator Pattern)
  • 中介者形式(Mediator Pattern)
  • 观察者形式(Observer Pattern
  • 备忘录形式(Memento Pattern)

职责链形式 (Chain of Responsibility Pattern)

职责链形式是一种经过方针链进行恳求处理的规划形式。

形式动机

为了防止恳求发送者与多个处理者耦合在一同,仅需将恳求产生给职责链首个方针,后续在该链的方针中传递处理,直到有某方针处理它停止。

处理问题

  • 客户恳求发送到方针职责链后,怎么做到无须关怀恳求的处理细节和恳求在方针职责链上的传递过程。
  • 方针职责链怎么将恳求的发送者与处理者进行解耦。

形式规划计划

电子科大软件体系架构规划——规划形式

  • 笼统处理者类 (Handler ) : 界说一个处理恳求的接口,包含笼统处理办法( handleRequest()) 和一个后继衔接(next)。
  • 详细处理者类(ConcreteHandler ) : 完结笼统处理者的处理办法,判断自己能否处理本次恳求。假如能够处理恳求则处理,否则将该恳求转给它的后继者。
  • 创立处理链,并向链头的详细处理者方针提交恳求,它不关怀处理细节和恳求的传递过程。

电子科大软件体系架构规划——规划形式

形式完结计划

package chainOfResponsibility;
public class ChainOfResponsibilityPattern {
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);
        handler1.handleRequest("two");
    }
}
abstract class Handler {
    private Handler next;
    public void setNext(Handler next) {
        this.next = next;
    }
    public Handler getNext() {
        return next;
    }
    public abstract void handleRequest(String request);
}
class ConcreteHandler1 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("one")) {
            System.out.println("详细处理者1担任处理恳求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("没有人处理该恳求!");
            }
        }
    }
}
class ConcreteHandler2 extends Handler {
    public void handleRequest(String request) {
        if (request.equals("two")) {
            System.out.println("详细处理者2担任处理恳求!");
        } else {
            if (getNext() != null) {
                getNext().handleRequest(request);
            } else {
                System.out.println("没有人处理该恳求!");
            }
        }
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

适用场景

  1. 有多个方针处理同一个恳求,详细哪个方针处理该恳求由运转时间依据音讯内容自动确认。
  2. 在不清晰指定接收者的情况下,向多个方针中的首个方针提交恳求。
  3. 可动态指定一组方针处理恳求,或增加新的处理者。

运用示例

电子科大软件体系架构规划——规划形式

  • 创立笼统类AbstractLogger,记载日志信息。
  • 创立三种日志类型的记载器,别离承继AbstractLogger笼统类。
  • 每个记载器接收音讯后,判断该音讯是否归于自己的类型。假如是则相应地打印出来。否则将不打印,并把音讯传给下一个记载器。
package ChainResponsibilityPattern;
// 创立笼统的记载器类
abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;
    protected int level;
    // 职责链中的下一个元素
    protected AbstractLogger nextLogger;
    public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }
    public void logMessage(int level, String message) {
        if (this.level <= level) {
            write(message);
        }
        if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }
    abstract protected void write(String message);
}
// 创立扩展了该记载器类的实体类
class ConsoleLogger extends AbstractLogger {
    public ConsoleLogger(int level) {
        this.level = level;
    }
    @Override
    protected void write(String message) {
        System.out.println("Standard Console::Logger: "   message);
    }
}
class ErrorLogger extends AbstractLogger {
    public ErrorLogger(int level) {
        this.level = level;
    }
    @Override
    protected void write(String message) {
        System.out.println("Error Console::Logger: "   message);
    }
}
class FileLogger extends AbstractLogger {
    public FileLogger(int level) {
        this.level = level;
    }
    @Override
    protected void write(String message) {
        System.out.println("File::Logger: "   message);
    }
}
// 运用职责链形式的不同记载器记载音讯
public class ChainPatternDemo {
    private static AbstractLogger getChainOfLoggers() {
        AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);
        return errorLogger;
    }
    public static void main(String[] args) {
        AbstractLogger loggerChain = getChainOfLoggers();
        loggerChain.logMessage(AbstractLogger.INFO, "这是一个信息日志信息。");
        loggerChain.logMessage(AbstractLogger.DEBUG, "这是一个 debug 日志信息。");
        loggerChain.logMessage(AbstractLogger.ERROR, "这是一个 error 日志信息。");
    }
}

运转成果:

形式优缺陷

长处:

  • 下降了恳求方针与处理方针之间的耦合度。一个恳求方针无须知道到底是哪一个方针理其恳求以及链的结构。

  • 能够依据需求增加新的恳求处理类,满足开闭准则

  • 当工作流程产生改动,能够动态地改动链内的成员或许调动它们的次序,也可动态地新增或许删除职责。

  • 职责链简化了方针之间的衔接。每个方针只需坚持一个指向其后继者的引证,不需坚持其他一切处理者的引证,这防止了运用众多的 if 或许if…else 语句。

  • 职责分担。每个类只需求处理自己该处理的工作,不该处理的传递给下一个方针完结,清晰各类的职责范围,契合类的单一职责准则。

缺陷:

  • 不能确保每个恳求必定被处理。由于一个恳求没有清晰的接收者,所以不能确保它必定会被处理,该恳求或许一直传到链的末端都得不到处理。

  • 对比较长的职责链,恳求的处理或许触及多个处理方针,体系性能将遭到必定影响。

  • 职责链树立的合理性要靠客户端来确保,增加了客户端编程的杂乱性,或许会由于职责链的错误设置而导致体系犯错,如或许会造成循环调用。

剖析如下请假条审批模块功用类图怎么运用职责链形式规划?怎么编写Java程序。

电子科大软件体系架构规划——规划形式

// 领导者是笼统处理者
abstract class Leader {
    protected Leader next;
    public void setNext(Leader next) {
        this.next = next;
    }
    public Leader getNext() {
        return next;
    }
    // 笼统处理恳求的办法
    public abstract void handleRequest(int LeaveDays);
}
// 详细处理者1:班主任
class ClassAdviser extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 2) { // 班主任能够同意最多2天的请假
            System.out.println("班主任同意了 "   LeaveDays   " 天的请假。");
        } else if (next != null) {
            next.handleRequest(LeaveDays);
        }
    }
}
// 详细处理者2:系主任
class DepartmentHead extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 5) { // 系主任能够同意最多5天的请假
            System.out.println("系主任同意了 "   LeaveDays   " 天的请假。");
        } else if (next != null) {
            next.handleRequest(LeaveDays);
        }
    }
}
// 详细处理者3:院长
class Dean extends Leader {
    @Override
    public void handleRequest(int LeaveDays) {
        if (LeaveDays <= 10) { // 院长能够同意最多10天的请假
            System.out.println("院长同意了 "   LeaveDays   " 天的请假。");
        } else {
            System.out.println("请假恳求 "   LeaveDays   " 天被回绝。");
        }
    }
}
// 客户端类
public class LeaveApprovalTest {
    public static void main(String[] args) {
        // 设置职责链
        Leader teacher1 = new ClassAdviser();
        Leader teacher2 = new DepartmentHead();
        Leader teacher3 = new Dean();
        teacher1.setNext(teacher2);
        teacher2.setNext(teacher3);
        // 建议恳求
        teacher1.handleRequest(8); // 这个恳求将由院长处理
    }
}

客户端类也可按如下办法书写:

public class LeaveApprovalTest {
    private static Leader getChainOfLeaders() {
        // 创立职责链
        Leader classAdviser = new ClassAdviser();
        Leader departmentHead = new DepartmentHead();
        Leader dean = new Dean();
        // 设置职责链
        classAdviser.setNext(departmentHead);
        departmentHead.setNext(dean);
        // 回来链的起始点
        return classAdviser;
    }
    public static void main(String[] args) {
        Leader chainOfLeaders = getChainOfLeaders();
        // 建议恳求
        chainOfLeaders.handleRequest(8); // 这个恳求将由院长处理
    }
}

班主任能够同意最多2天的请假,系主任能够同意最多5天的请假,院长能够同意最多10天的请假。假如有一个恳求是请8天假,班主任会将它传递给系主任,系主任再传递给院长进行同意。假如恳求超出了同意才能,就会被回绝。

中介者形式(Mediator Pattern)

中介者形式是一种经过中介方针来封装若干方针之间的交互,完结方针之间的松耦合,而且能够独登时改动它们之间交互的规划形式。

形式动机

在一些运用中,多个方针之间存在较杂乱的网状交互联系。假如把这种’网状结构”改为“星形结构”,可下降它们之间的“耦合性”。这时只需找一个“中介者”来完结。

处理首要问题

  • 怎么处理多个方针之间存在大量的相相联系。
  • 若一个方针产生改动,怎么盯梢与之相相关的方针,并做出相应的处理。

形式规划计划

电子科大软件体系架构规划——规划形式

  • 笼统中介者 (Mediator) : 供给了搭档方针注册与转发搭档方针信息的笼统办法。

  • 详细中介者 (Concrete Mediator) :完结笼统中介者类,界说一个 List 来办理搭档方针,和谐各个搭档人物之间的交互联系。

  • 笼统搭档类(Colleague) : 界说搭档类的笼统办法,引证中介者方针,完结搭档类的公共功用。

  • 详细搭档类 (Concrete Colleague) : 笼统搭档类的完结者,当搭档方针交互时由中介者方针担任转发之间的交互。

形式完结计划

package mediator;
import java.util.*;
// 中介者形式
public class MediatorPattern {
    public static void main(String[] args) {
        Mediator md = new ConcreteMediator();
        Colleague c1, c2;
        c1 = new ConcreteColleague1();
        c2 = new ConcreteColleague2();
        md.register(c1);
        md.register(c2);
        c1.send();
        System.out.println("---------------");
        c2.send();
    }
}
// 笼统中介者
abstract class Mediator {
    public abstract void register(Colleague colleague);
    public abstract void relay(Colleague cl); // 转发
}
// 详细中介者
class ConcreteMediator extends Mediator {
    private List<Colleague> colleagues = new ArrayList<Colleague>();
    public void register(Colleague colleague) {
        if (!colleagues.contains(colleague)) {
            colleagues.add(colleague);
            colleague.setMedium(this);
        }
    }
    public void relay(Colleague cl) {
        for (Colleague ob : colleagues) {
            if (!ob.equals(cl)) {
                ((Colleague) ob).receive();
            }
        }
    }
}
// 笼统搭档类
abstract class Colleague {
    protected Mediator mediator;
    public void setMedium(Mediator mediator) {
        this.mediator = mediator;
    }
    public abstract void receive();
    public abstract void send();
}
// 详细搭档类1
class ConcreteColleague1 extends Colleague {
    public void receive() {
        System.out.println("详细搭档类1收到恳求。");
    }
    public void send() {
        System.out.println("详细搭档类1宣布恳求。");
        mediator.relay(this); // 恳求中介者转发
    }
}
// 详细搭档类2
class ConcreteColleague2 extends Colleague {
    public void receive() {
        System.out.println("详细搭档类2收到恳求。");
    }
    public void send() {
        System.out.println("详细搭档类2宣布恳求。");
        mediator.relay(this); // 恳求中介者转发
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

适用场景

  1. 当方针之间存在杂乱的网状结构联系而导致通讯联系杂乱。
  2. 经过一个中心类来封装多个类中的行为,而又不想生成太多的子类。

运用示例

电子科大软件体系架构规划——规划形式

  • 多个用户能够经过聊天室进行音讯沟通聊天室向一切的用户显示音讯。

  • 创立两个类 ChatRoom 和 User。

  • User 方针 运用 ChatRoom 方针的showMessage()办法来分享他们的音讯。

  • MediatorPatternDemo演示类运用 User方针来显示他们之间的通讯。

package MediatorPattern;
import java.util.Date;
// 聊天室类充任中介者
class ChatRoom {
    public static void showMessage(User user, String message) {
        System.out.println(new Date().toString()   " ["   user.getName()   "]: "   message);
    }
}
// 用户类
class User {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public User(String name) {
        this.name = name;
    }
    public void sendMessage(String message) {
        ChatRoom.showMessage(this, message);
    }
}
// 中介者形式演示
public class MediatorPatternDemo {
    public static void main(String[] args) {
        User robert = new User("Robert");
        User john = new User("John");
        robert.sendMessage("Hi! John!");
        john.sendMessage("Hello! Robert!");
    }
}

运转示例:

电子科大软件体系架构规划——规划形式

形式优缺陷

长处:

  • 类之间各司其职,契合迪米特法则。

  • 下降了方针之间的耦合性,使得方针易于独登时被复用。

  • 将方针间的一对多相关转变为1对1的相关,进步体系的灵活性,使得体系易于保护和扩展。

缺陷:

  • 中介者形式将原本多个方针直接的彼此依靠变成了中介者和多个方针之间的依靠联系。
  • 当交互的方针越多时,中介者就会越臃肿,变得杂乱且难以保护。

剖析如下“房地产沟通”模块功用类图怎么运用中介者形式规划?给出Java程序。

电子科大软件体系架构规划——规划形式

import java.util.ArrayList;
import java.util.List;
// 中介者接口
interface Medium {
    void register(Customer member);
    void relay(String from, String ad);
}
// 房地产中介
class EstateMedium implements Medium {
    private List<Customer> members = new ArrayList<>();
    @Override
    public void register(Customer member) {
        if (!members.contains(member)) {
            members.add(member);
            member.setMedium(this);
        }
    }
    @Override
    public void relay(String from, String ad) {
        for (Customer member : members) {
            if (!member.getName().equals(from)) {
                member.receive(from, ad);
            }
        }
    }
}
// 客户类
abstract class Customer {
    protected Medium medium;
    protected String name;
    public Customer(String name) {
        this.name = name;
    }
    public abstract void send(String ad);
    public abstract void receive(String from, String ad);
    public String getName() {
        return name;
    }
    public void setMedium(Medium medium) {
        this.medium = medium;
    }
}
// 卖方类
class Seller extends Customer {
    public Seller(String name) {
        super(name);
    }
    @Override
    public void send(String ad) {
        System.out.println("Seller ["   this.name   "] sends message: "   ad);
        medium.relay(this.name, ad);
    }
    @Override
    public void receive(String from, String ad) {
        System.out.println("Seller ["   this.name   "] received message from "   from   ": "   ad);
    }
}
// 买方类
class Buyer extends Customer {
    public Buyer(String name) {
        super(name);
    }
    @Override
    public void send(String ad) {
        System.out.println("Buyer ["   this.name   "] sends message: "   ad);
        medium.relay(this.name, ad);
    }
    @Override
    public void receive(String from, String ad) {
        System.out.println("Buyer ["   this.name   "] received message from "   from   ": "   ad);
    }
}
// 中介者形式演示
public class MediatorPattern {
    public static void main(String[] args) {
        Medium medium = new EstateMedium();
        Customer seller = new Seller("Alice");
        Customer buyer = new Buyer("Bob");
        medium.register(seller);
        medium.register(buyer);
        seller.send("House for sale!");
        buyer.send("Looking for a house!");
    }
}

运转成果:

电子科大软件体系架构规划——规划形式

课堂作业与作业练习

一、单选题

1.哪种规划准则要求面向接口编程,而不要面向完结编程?(C)

A.开闭准则

B.里氏替换准则

C.依靠倒置准则

D.接口别离准则

2.哪种规划准则要求没有直接联系的类就不能直接彼此调用?(B)

A.里氏替换准则

B.迪米特法则

C. 依靠倒置准则

D.开闭准则

3.下面哪个不是发现类的办法?(C)

A. CRC办法

B.用例驱动法

C.脑筋风暴

D.公共类形式法

4.子类组合来自超类的特征,并重载部分承继来的特征,该承继称为什么?(C)

A.扩展承继

B.方便承继

C.限制承继

D.以上都不是

5.下面哪一个操作符用于界说条件片段?(C)

A. opt

B. para

C. alt

D. loop

二、判断题

1.单例形式归于规划形式的行为形式类别。()

2.适配器形式能够处理不同模块的类接口转化问题。(√)

3.桥接形式是一种用于把笼统部分与完结部分解耦的规划形式。(√)

4.职责链形式要求恳求发送者与多个恳求处理者直接交互。()

5.中介者形式的动机用于下降多个方针之间存在较杂乱的联系。(√)

6.处于相同状况的同类的不同方针对同一事情的反响往往是一样的,而处于不同状况的同一方针则对同一事情会做出不同反响。(√)

7.只需将包中元素的可见性设为公共的,则其他包就能够拜访它。()

8.聚合与泛化都是面向方针体系支撑功用复用的强大技术。()

9.在UML构件图中,需求界说音讯来描绘构件之间的联系。()

10.一切方针都经过类来描绘,一切类都具有方针。()

补充:

6.处于相同状况的方针对同一事情具有相同办法的反响,所以当给定状况下的多个方针当接遭到相搭档件时会履行相同的动作,例如:当一个在线视频播映器处于”播映中”状况时,对于”暂停”按钮的点击事情,一切这类播映器方针都会履行相同的动作,即暂停当时视频。不管哪个播映器实例,只需它正在播映视频,点击”暂停”按钮都会导致视频停止播映。但是处于不同状况下的方针会经过不同的动刁难同一事情做出不同的反响。例如,当自动答复机处于处理事务状况或空闲状况时会对撤销键做出不同的反响。

7.虽然将类或成员的可见性设为公共(public)能够让其他包中的类拜访这些元素,但这并不是仅有的条件。其他包中的类还需求正确地导入包含公共元素的包。

8.这道题网上搜出来都是对的,答案给的错,后面再看看吧。

10.一切方针都是由类来描绘的,这是正确的;但不是一切类都具有方针实例。有些类或许从未被实例化,尤其是笼统类和东西类(或许只需静态办法和成员)。

三、填空题

(状况机图)经过对方针的各种状况树立模型来描绘方针随时间改动的动态行为,而且它是以独立的方针为中心进行描绘的。

在UML类图中,类用矩形图符来表明,这个矩形由3个部分组成,别离是类型名、(属性)和操作。

UML中的交互图包含次序图和(通讯图)。

UML中次序图表明为二维图,纵向是方针,横向代表参与交互方针之间的(音讯)。

状况机图由方针的状况和衔接这些状况的(转化)组成。