Java中的Facade Design Pattern


在这里,我还有另一篇关于设计模式的文章- 外观设计模式。甲门面 目的是使用通过隐藏一个复杂的系统的复杂性,以提供简单的界面。

Facade Design Pattern

  • 该Facade 是一个结构设计模式和一个四设计模式岗。
  • Facade 对象用于通过掩蔽更复杂的底层系统,以提供一个前置接口。
  • Facade 可提供有限的或专用的功能集。但是,Facade提供的功能主要是客户端应用程序所需的。因此,它可以根据客户需求提供更多服务。
  • Facade 的主要目的是通过提供更简单的界面来处理,以隐藏系统/子系统的复杂性。
  • 当我们必须处理具有许多功能和不同配置的复杂系统/子系统时,使用Facade 非常容易。
  • 因此,Facade 隐藏了我们应该知道的任何第三方库,系统或子系统的次要和内部细节。
  • 在Java中,有许多功能,如JDBC,JPA,JAX-RS等,这些功能隐藏了次要细节,并以注释形式或易于配置的方式提供了更简单的界面。
  • 甚至在我们启动系统时运行的计算机系统的POST(开机自检)过程;是Facade的一个很好的例子。在控制操作系统之前,它会检查RAM,CPU,HDD和其他连接的外围设备。
  • Facade通过Facade引入了附加的抽象层。因此,如果子系统发生更改,我们还需要在外观层中进行相应的更改。

facadedesignpattern.png

我们可能还会有多个Facade对象,一个对象处理几个子系统,另一个对象处理一些其他子系统。

我希望我们现在清楚什么是Facade?为了更清楚地了解它以及在我们的代码中使用Facade,让我们以我们通常在家中使用的家用电器为例。

Home Appliance Application using Facade Design Pattern 为了更容易地理解Facade的用法,我在这里使用示例示例应用程序代码(在Command Design Pattern中使用了示例代码)。我只做了一些必要的更改,并添加了其他设备和功能的代码,以使示例更加清晰有趣。

该示例还帮助您将Facade与Command Design Pattern进行比较。

Code for Appliance class:

package org.trishinfotech.facade.devices;
public abstract class Appliance implements Comparable<Appliance> {

    protected String name;
    protected boolean status;

    public Appliance(String name) {
        super();
        if (name == null || name.trim().isEmpty()) {
            new IllegalArgumentException("Appliance name is mandatory for Home Automation");
        }
        this.name = name;
    }

    public String name() {
        return name;
    }

    // define operations for appliance
    public void on() {
        if (status) {
            System.out.printf("'%s' is already turned on!\n", name);

        } else {
            status = true;
            System.out.printf("Turning On '%s'\n", name);
        }
    }

    public void off() {
        if (!status) {
            System.out.printf("'%s' is already turned off!\n", name);
        } else {
            status = false;

            System.out.printf("Turning Off '%s'\n", name);
        }
    }

    // Appliance should be compared only on name.
    @Override
    public int compareTo(Appliance other) {
        return this.name.compareToIgnoreCase(other.name);
    }

}

我在这里定义了一些常见的操作,例如“开”和“关”。

粉丝等级代码:

package org.trishinfotech.facade.devices;
public abstract class Fan extends Appliance {
    public static int TOP_SPEED = 4;

    public static int LOWEST_SPEED = 1;

    protected int currentSpeed = 1;

    public Fan(String name) {
        super(name);
    }

    // define operations for fan
    public void increase() {
        if (currentSpeed < TOP_SPEED) {
            currentSpeed++;
            System.out.printf("Encreasing Speed of '%s' to '%d'.\n", name, currentSpeed);
        } else {
            System.out.printf("'%s' is already running at top speed!\n", name);
        }
    }

    public void decrease() {
        if (currentSpeed > LOWEST_SPEED) {
            currentSpeed--;
            System.out.printf("Decreasing Speed of '%s' to '%d'.\n", name, currentSpeed);
        } else {
            System.out.printf("'%s' is laready running at lowest speed!\n", name);
        }
    }
}

我在此处添加了“增加”和“减少”速度等操作,以及它的“设备”子类型。

Code for Light class:

package org.trishinfotech.facade.devices;
public abstract class Light extends Appliance {

    public Light(String name) {
        super(name);
    }
}

由于我们已经定义了“开”和“关”,因此无需进行其他操作。

SoundBar 类的代码:

package org.trishinfotech.facade.devices;
public abstract class SoundBar extends Appliance {
    public static int TOP_VOLUME = 30;
    public static int LOWEST_VOLUME = 0;
    protected String soundMode;
    protected int currentVolume = 1;
    protected int volumeWhenMute;
    public SoundBar(String name) {
        super(name);
    }

    // define operations for SoundBar
    public void setSoundMode(String soundMode) {
        this.soundMode = soundMode;
        System.out.printf("Setting Sound-Mode of '%s' to '%s'.\n", name, soundMode);
    }

    public void increaseVolume() {
        if (currentVolume < TOP_VOLUME) {
            currentVolume++;
            System.out.printf("Encreasing volume of '%s' to '%d'.\n", name, currentVolume);
        } else {
            System.out.printf("'%s' is already on top volume!\n", name);
        }
    }

    public void decreaseVolume() {
        if (currentVolume > LOWEST_VOLUME) {
            currentVolume--;
            System.out.printf("Decreasing volume of '%s' to '%d'.\n", name, currentVolume);
        } else {
            System.out.printf("'%s' is already on mute!\n", name);
        }
    }

    public void volume(int volume) {
        if (volume >= LOWEST_VOLUME && volume <= TOP_VOLUME) {
            currentVolume = volume;
            System.out.printf("Setting volume of '%s' to '%d'.\n", name, currentVolume);
        } else {
            System.out.printf("Volume of '%s' is supports range between '%d' and '%d'!\n", name, LOWEST_VOLUME,
                    TOP_VOLUME);
        }
    }

    public void mute() {
        if (currentVolume != LOWEST_VOLUME) {
            volumeWhenMute = currentVolume;
            currentVolume = 0;
            System.out.printf("Putting '%s' on mute!\n", name);
        } else {
            currentVolume = volumeWhenMute;
            System.out.printf("Unmuting '%s'. Setting volume back to '%d'!\n", name, currentVolume);
        }
    }

    public String soundMode() {
        return soundMode;
    }
}

在这里,我为“声音模式”和“增加”,“减少”,“设置”音量和“静音”等操作添加了代码。

Code for TV class:

package org.trishinfotech.facade.devices;
public abstract class TV extends Appliance {
    public static int TOP_VOLUME = 30;
    public static int LOWEST_VOLUME = 0;
    public static int TOP_CHANNEL_NO = 999;
    public static int LOWEST_CHANNEL_NO = 1;
    protected int currentVolume = 1;
    protected int currentChannel = 1;
    protected int volumeWhenMute;
    public TV(String name) {
        super(name);
    }

    // define operations for TV
    public void increaseVolume() {
        if (currentVolume < TOP_VOLUME) {
            currentVolume++;
            System.out.printf("Encreasing volume of '%s' to '%d'.\n", name, currentVolume);
        } else {
            System.out.printf("'%s' is already on top volume!\n", name);
        }
    }

    public void decreaseVolume() {
        if (currentVolume > LOWEST_VOLUME) {
            currentVolume--;
            System.out.printf("Decreasing volume of '%s' to '%d'.\n", name, currentVolume);
        } else {
            System.out.printf("'%s' is already on mute!\n", name);
        }
    }

    public void mute() {
        if (currentVolume != LOWEST_VOLUME) {
            volumeWhenMute = currentVolume;
            currentVolume = 0;
            System.out.printf("Putting '%s' on mute!\n", name);
        } else {
            currentVolume = volumeWhenMute;
            System.out.printf("Unmuting '%s'. Setting volume back to '%d'!\n", name, currentVolume);
        }
    }

    public void increaseChannel() {
        if (currentChannel < TOP_CHANNEL_NO) {
            currentChannel++;
            System.out.printf("Encreasing channel of '%s' to '%d'.\n", name, currentChannel);
        } else {
            System.out.printf("'%s' is already showing channel '%d'!\n", name, currentChannel);
        }
    }

    public void decreaseChannel() {
        if (currentChannel > LOWEST_CHANNEL_NO) {
            currentChannel--;
            System.out.printf("Decreasing channel of '%s' to '%d'.\n", name, currentChannel);
        } else {
            System.out.printf("'%s' is already showing channel '%d'!\n", name, currentChannel);
        }
    }
}

我添加了“增加/减少音量”,“增加/减少频道”和“静音”之类的操作。

CoffeeMaker类的代码:

package org.trishinfotech.facade.devices.kitchen;
import org.trishinfotech.facade.devices.Appliance;
public class CoffeeMaker extends Appliance {

    public CoffeeMaker() {
        super("CoffeeMaker");
    }

}

ElectricGrill类的代码:

package org.trishinfotech.facade.devices.kitchen;
import org.trishinfotech.facade.devices.Appliance;
public class ElectricGrill extends Appliance {
    protected int temp;

    public ElectricGrill() {
        super("ElectricGrill");
    }
    public void setTemp(int temp) {
        this.temp = temp;
        System.out.printf("Setting '%s' temprature to '%d'.\n", name, temp);
    }

    public int temperature() {
        return temp;
    }
}

在这里,我添加了设置“温度”的操作。

KitchenLight类的代码:

package org.trishinfotech.facade.devices.kitchen;
import org.trishinfotech.facade.devices.Light;
public class KitchenLight extends Light {
    public KitchenLight() {
        super("KitchenLight");
    }

}

Code for Microwave class:

package org.trishinfotech.facade.devices.kitchen;
import org.trishinfotech.facade.devices.Appliance;
public class Microwave extends Appliance {

    protected int temp;
    protected int time;
    protected boolean grillOn = false;

    public Microwave() {
        super("Microwave");
    }
    public void grillOn() {
        this.grillOn = true;
        System.out.printf("Turning on grill of '%s'.\n", name);
    }

    public void grillOff() {

        this.grillOn = false;
        System.out.printf("Turning off grill of '%s'.\n", name);
    }

    public void setOnPreHeat(int temp, int time) {
        this.temp = temp;
        this.time = time;
        System.out.printf("Setting '%s' on Pre-Heat, temprature '%d', time '%d' minutes.\n", name, temp, time);
    }

    public void bake(String pizzaName, int temp, int time) {
        this.temp = temp;
        this.time = time;
        System.out.printf("Baking '%s' in '%s' for temprature '%d', time '%d' minutes.\n", pizzaName, name, temp, time);
    }

    public int temp() {
        return temp;
    }

    public int time() {
        return time;
    }
}

在这里,我定义了诸如“烧烤开/关”,“预加热”和“烘焙”之类的操作。

Code for Refrigerator class:

package org.trishinfotech.facade.devices.kitchen;
import org.trishinfotech.facade.devices.Appliance;
public class Refrigerator extends Appliance {
    protected static final String PARTY = "party";
    protected static final String NORMAL = "normal";
    protected String mode = NORMAL;

    public Refrigerator() {
        super("Refrigerator");
    }

    public void partyMode() {
        mode = PARTY;
        System.out.printf("Setting '%s' Cooling to 'Party'.\n", name);
    }

    public void normalMode() {
        mode = NORMAL;
        System.out.printf("Setting '%s' Cooling to 'Normal'.\n", name);
    }
}

在这里,我定义了“模式”(如“派对”)进行快速冷却,并定义了“普通”(针对正常冷却)。

LivingRoomFan类的代码:

package org.trishinfotech.facade.devices.livingroom;
import org.trishinfotech.facade.devices.Fan;
public class LivingRoomFan extends Fan {
    public LivingRoomFan() {
        super("LivingRoomFan");
    }

}

LivingRoomFireTV4KStick类的代码:

package org.trishinfotech.facade.devices.livingroom;
import org.trishinfotech.facade.devices.Appliance;
import org.trishinfotech.facade.devices.TV;
public class LivingRoomFireTV4KStick extends Appliance {

    protected TV tv;
    protected String appName;
    protected String contentName;
    public LivingRoomFireTV4KStick(TV tv) {
        super("LivingRoomFireTV4KStick");
        this.tv = tv;
    }

    // define operations for Fire TV Stick 4K
    public void openApp(String appName) {
        this.appName = appName;
        System.out.printf("Opening '%s' on '%s'.\n", appName, name);
    }

    public void selectContent(String contentName) {
        this.contentName = contentName;
        System.out.printf("Searching '%s' on '%s'.\n", contentName, appName);
    }

    public void play() {
        System.out.printf("Playing '%s' on '%s'.\n", contentName, appName);
    }

    public void closeApp() {
        System.out.printf("Closing '%s' on '%s'.\n", appName, name);
    }

    public TV tv() {
        return tv;
    }
    public String appName() {
        return appName;
    }

    public String contentName() {
        return contentName;
    }
}

在这里,我添加了“打开应用程序”,“关闭应用程序”,“搜索内容”和“播放”之类的操作。

LivingRoomLight类的代码:

package org.trishinfotech.facade.devices.livingroom;
import org.trishinfotech.facade.devices.Light;
public class LivingRoomLight extends Light {
    protected int brightness = 50;

    public LivingRoomLight() {
        super("LivingRoomLight");
    }

    public void dim() {
        brightness = 20;
        System.out.printf("Dimming '%s'.\n", name);
    }

    public void bright() {
        brightness = 100;
        System.out.printf("Setting brightness of '%s' to '%d'.\n", name, brightness);
    }

}

在这里,我定义了通过使用“暗淡”和“明亮”来控制灯光亮度的操作。

LivingRoomSoundBar类的代码:

package org.trishinfotech.facade.devices.livingroom;
import org.trishinfotech.facade.devices.SoundBar;
import org.trishinfotech.facade.devices.TV;
public class LivingRoomSoundBar extends SoundBar {
    protected TV tv;

    public LivingRoomSoundBar(TV tv) {
        super("LivingRoomSoundBar");
        this.tv = tv;
    }
    public TV tv() {
        return tv;
    }

}

LivingRoomTV类的代码:

package org.trishinfotech.facade.devices.livingroom;
import org.trishinfotech.facade.devices.TV;
public class LivingRoomTV extends TV {
    protected String source;
    public LivingRoomTV() {
        super("LivingRoomTV");
    }

    public void setSource(String source) {
        this.source = source;
        System.out.printf("Setting Source of '%s' to '%s'.\n", name, source);
    }
    public String source() {
        return source;
    }
}

现在,在定义了所有设备及其操作之后,就该使用Facade Design Pattern了。假设我们喜欢在家里与朋友和家人一起参加周末聚会。由于我们在家中拥有用于娱乐和餐饮的各种设备,因此我们编写了一个HomeFacade 来定义“周末家庭聚会”的操作。

HomeFacade类的代码:

package org.trishinfotech.facade;
import java.util.List;
import org.trishinfotech.facade.devices.Fan;
import org.trishinfotech.facade.devices.Light;
import org.trishinfotech.facade.devices.SoundBar;
import org.trishinfotech.facade.devices.TV;
import org.trishinfotech.facade.devices.kitchen.CoffeeMaker;
import org.trishinfotech.facade.devices.kitchen.ElectricGrill;
import org.trishinfotech.facade.devices.kitchen.KitchenLight;
import org.trishinfotech.facade.devices.kitchen.Microwave;
import org.trishinfotech.facade.devices.kitchen.Refrigerator;
import org.trishinfotech.facade.devices.livingroom.LivingRoomFan;
import org.trishinfotech.facade.devices.livingroom.LivingRoomFireTV4KStick;
import org.trishinfotech.facade.devices.livingroom.LivingRoomLight;
import org.trishinfotech.facade.devices.livingroom.LivingRoomSoundBar;
import org.trishinfotech.facade.devices.livingroom.LivingRoomTV;
public class HomeFacade {

    Fan fan;
    LivingRoomFireTV4KStick stick;
    Light livingRoomLight;
    SoundBar soundBar;
    TV tv;

    CoffeeMaker maker;
    ElectricGrill grill;
    Light kitchenLight;
    Microwave microwave;
    Refrigerator refrigerator;

    public HomeFacade() {
        super();
        fan = new LivingRoomFan();
        tv = new LivingRoomTV();
        stick = new LivingRoomFireTV4KStick(tv);
        livingRoomLight = new LivingRoomLight();
        soundBar = new LivingRoomSoundBar(tv);

        maker = new CoffeeMaker();
        grill = new ElectricGrill();
        kitchenLight = new KitchenLight();
        microwave = new Microwave();
        refrigerator = new Refrigerator();
    }

    public void playMovieOnNetflix(String movieName) {
        fan.on();
        fan.increase();
        livingRoomLight.on();
        tv.on();
        ((LivingRoomTV)tv).setSource("HDMI ARC");
        stick.on();
        soundBar.on();
        soundBar.setSoundMode("Dolby Atmos");
        stick.openApp("Netflix");
        stick.selectContent(movieName);
        ((LivingRoomLight)livingRoomLight).dim();
        soundBar.volume(20);
        stick.play();
    }

    public void prepareFood(List<String> pizzaNames) {
        kitchenLight.on();
        // normally refrigerator runs always. so no need to turn on.
        refrigerator.partyMode(); // for fast cooling
        microwave.on();
        microwave.setOnPreHeat(200, 5);
        microwave.grillOn();
        grill.on();
        maker.on();
        pizzaNames.forEach(pizzaName -> microwave.bake(pizzaName, 400, 10));
    }

    public void stopMovie() {
        stick.closeApp();
        stick.off();
        soundBar.off();

        tv.off();
        ((LivingRoomLight)livingRoomLight).bright();
        fan.off();
    }

    public void closeKitchen() {
        refrigerator.normalMode();
        grill.off();
        maker.off();
        microwave.off();
    }
}

这是我们要处理的 立面方法

设置家庭娱乐系统以播放电影。 为家人和朋友准备食物。 电影完成后,关闭家庭娱乐系统。 关闭厨房电器后,我们的家庭聚会就会发布。 现在,是时候编写我们的Main 应用程序来执行HomeFacade 并测试输出了:

package org.trishinfotech.facade;
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        HomeFacade home = new HomeFacade();
        System.out.println("Weekend: Enjoying with friends and family at home...");
        System.out.println("-----------------------------------------------------------------");

        System.out.println("Setting up movie...");
        home.playMovieOnNetflix("Spider-Man: Far From Home");
        System.out.println("-----------------------------------------------------------------");
        System.out.println("Preparing food...");
        home.prepareFood(Arrays.asList("Napoletana Pizza", "Margherita Pizza", "Marinara Pizza",
                "Chicago-Style Deep Dish Pizza"));
        System.out.println("-----------------------------------------------------------------");
        System.out.println("Enjoy Movie with Meal and Drink...");
        System.out.println("Movie Completed.");
        System.out.println("-----------------------------------------------------------------");
        System.out.println("Stopping Movie...");
        home.stopMovie();
        System.out.println("-----------------------------------------------------------------");
        System.out.println("Closing Kitchen...");
        home.closeKitchen();
        System.out.println("-----------------------------------------------------------------");
        System.out.println("Done!");
    }

}

下面是程序的输出:

Weekend: Enjoying with friends and family at home...
-----------------------------------------------------------------
Setting up movie...
Turning On 'LivingRoomFan'
Encreasing Speed of 'LivingRoomFan' to '2'.
Turning On 'LivingRoomLight'
Turning On 'LivingRoomTV'
Setting Source of 'LivingRoomTV' to 'HDMI ARC'.
Turning On 'LivingRoomFireTV4KStick'
Turning On 'LivingRoomSoundBar'
Setting Sound-Mode of 'LivingRoomSoundBar' to 'Dolby Atmos'.
Opening 'Netflix' on 'LivingRoomFireTV4KStick'.
Searching 'Spider-Man: Far From Home' on 'Netflix'.
Dimming 'LivingRoomLight'.
Setting volume of 'LivingRoomSoundBar' to '20'.
Playing 'Spider-Man: Far From Home' on 'Netflix'.
-----------------------------------------------------------------
Preparing food...
Turning On 'KitchenLight'
Setting 'Refrigerator' Cooling to 'Party'.
Turning On 'Microwave'
Setting 'Microwave' on Pre-Heat, temprature '200', time '5' minutes.
Turning On 'ElectricGrill'
Turning On 'CoffeeMaker'
Baking 'Napoletana Pizza' in 'Microwave' for temprature '400', time '10' minutes.
Baking 'Margherita Pizza' in 'Microwave' for temprature '400', time '10' minutes.
Baking 'Marinara Pizza' in 'Microwave' for temprature '400', time '10' minutes.
Baking 'Chicago-Style Deep Dish Pizza' in 'Microwave' for temprature '400', time '10' minutes.
-----------------------------------------------------------------
Enjoy Movie with Meal and Drink...
Movie Completed.

-----------------------------------------------------------------
Stopping Movie...
Closing 'Netflix' on 'LivingRoomFireTV4KStick'.
Turning Off 'LivingRoomFireTV4KStick'
Turning Off 'LivingRoomSoundBar'
Turning Off 'LivingRoomTV'
Setting brightness of 'LivingRoomLight' to '100'.
Turning Off 'LivingRoomFan'
-----------------------------------------------------------------
Closing Kitchen...
Setting 'Refrigerator' Cooling to 'Normal'.
Turning Off 'ElectricGrill'
Turning Off 'CoffeeMaker'
Turning Off 'Microwave'
-----------------------------------------------------------------
Done!

而已!

源代码可以在这里找到:Facade-Design-Pattern-Sample-Code

常见问题解答

  1. Facade Pattern Vs Adapter Pattern
    • Adapter Pattern允许使不兼容的系统兼容。因此,我们修复了系统与客户端应用程序的兼容性问题。没有适配器,我们将无法使用系统(不兼容)。适配器通常与一个对象一起使用。阅读有关适配器设计模式的更多信息。
    • Facade Pattern 简化了系统的复杂性(兼容但复杂)。没有Facade,我们仍然可以使用该系统。但这需要我们了解许多次要和内在的细节。外立面可与整个系统配合使用。
  2. Facade Pattern Vs Command Pattern
    • Facade Pattern隐藏内部细节并提供简化的界面。
    • Command Pattern封装了执行任务所需的动作(不可撤销的动作集)。
  3. Facade Pattern Vs Mediator Pattern
    • Facade Pattern 定义了简化到复杂系统的接口。
    • Mediator Pattern提供了系统组件之间的中央通信点。
  4. Facade Pattern Vs Flyweight Pattern
    • Flyweight Pattern可为系统创建较小的可重用对象。
    • Facade Pattern 创建单个较大的对象来处理整个系统。
  5. Facade Pattern Vs Proxy Pattern
    • Proxy Pattern 与Facade相似,除了它提供与服务对象相同的接口以使复杂的对象可互换。
  6. Facade Pattern Vs Abstract Factory
    • Abstract Factory 似于Facade,除了它只处理系统/子系统对象的创建部分。
    • Facade也处理系统对象的操作部分。
  7. Facade Pattern Vs Singleton Pattern 通常,我们在实现时将Facade Object创建为Singleton,因为它用于其目的。 我希望本教程演示外观设计模式的使用。


原文链接:http://codingdict.com