本章节给您介绍23种设计模式的结构型模式中剩余的3种设计模式,分别为:外观模式
、组合模式
和享元模式
。
如有帮助记得3连 加 关注哦!
第一章:7种设计原则之单一职责原则、接口隔离原则、依赖倒置原则、里氏替换原则
第二章:7种设计原则之开闭原则、迪米特法则、合成复用原则
第三章:设计模式之单例模式、工厂模式、原型模式、建造者模式
第四章:设计模式之适配器模式、桥接模式、代理模式、装饰者模式
在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了。
软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。
外观(Facade)模式又叫作门面模式
,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
通过智能家居案例实现:家中有点电视、空调、电灯等家电,当我们说打开时,所有家电都打开,说关闭时所有家电都关闭。
其实就是通过一个操作入口,内部聚合了一系列的操作,不妨想一想智能语音助手。
// 灯类
public class Light {
public void on() {
System.out.println("打开灯。。。。。。");
}
public void off() {
System.out.println("关闭灯。。。。。。");
}
}
// 电视类
public class TV {
public void on() {
System.out.println("打开电视。。。。。。");
}
public void off() {
System.out.println("关闭电视。。。。。。");
}
}
// 空调类
public class AirCondition {
public void on() {
System.out.println("打开空调。。。。。。");
}
public void off() {
System.out.println("关闭空调。。。。。。");
}
}
// 门面类
public class SmartAppliancesFacade {
// 聚合
private Light light;
private TV tv;
private AirCondition airCondition;
public SmartAppliancesFacade() {
this.light = new Light();
this.tv = new TV();
this.airCondition = new AirCondition();
}
public void say(String msg) {
if("打开".equals(msg)) {
on();
}else if("关闭".equals(msg)) {
off();
}else {
System.out.println("我还听不懂你在说什么");
}
}
private void on() {
light.on();;
tv.on();
airCondition.on();
}
private void off() {
light.off();
tv.off();
airCondition.off();
}
}
// 客户端类
public class Client {
public static void main(String[] args) {
SmartAppliancesFacade facade = new SmartAppliancesFacade();
facade.say("打开");
System.out.println("===睡觉===");
facade.say("关闭");
}
}
优点
缺点
在现实生活中,存在很多“部分-整体”的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣服与衣柜、以及厨房中的锅碗瓢盆等。在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。
组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式
,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性。
通过一个 管理系统 左侧菜单栏的案例实现
// 组件菜单:属于抽象根节点
public abstract class MenuComponent {
// 菜单组件名称
protected String name;
// 菜单组件层级
protected int level;
// 添加子菜单
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
// 移除子菜单
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
// 获取指定的子菜单
public MenuComponent getChild(int index) {
throw new UnsupportedOperationException();
}
// 获取菜单或菜单项的名称
public String getName() {
return name;
}
// 打印菜单名称的方法(包含子菜单和子菜单项)
public abstract void print();
}
// 菜单类:属于树枝节点
public class Menu extends MenuComponent{
// 菜单可以有子菜单
private List<MenuComponent> menuComponentList = new ArrayList<>();
public Menu(String name,int level) {
this.name = name;
this.level = level;
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int index) {
return menuComponentList.get(index);
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
// 打印本菜单名称
System.out.println(name);
// 打印子菜单名
for (MenuComponent menuComponent : menuComponentList) {
menuComponent.print();
}
}
}
// 菜单项类
public class MenuItem extends MenuComponent{
public MenuItem(String name,int level) {
this.name = name;
this.level = level;
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
// 打印名称
System.out.println(name);
}
}
// 创建对应的菜单,指定层级,并添加菜单项
public class Client {
public static void main(String[] args) {
MenuComponent menu1 = new Menu("菜单管理", 2);
menu1.add(new MenuItem("页面访问",3));
menu1.add(new MenuItem("展开菜单",3));
menu1.add(new MenuItem("编辑菜单",3));
menu1.add(new MenuItem("删除菜单",3));
menu1.add(new MenuItem("新增菜单",3));
MenuComponent menu2 = new Menu("权限管理", 2);
menu2.add(new MenuItem("页面访问",3));
menu2.add(new MenuItem("提交保存",3));
MenuComponent menu3 = new Menu("角色管理", 2);
menu3.add(new MenuItem("页面访问",3));
menu3.add(new MenuItem("新增角色",3));
menu3.add(new MenuItem("修改角色",3));
// 创建1级菜单
MenuComponent menu = new Menu("系统管理", 1);
menu.add(menu1);
menu.add(menu2);
menu.add(menu3);
menu.print();
}
}
优点
缺点
在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。
享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
案例:俄罗斯方块中有固定的几种形状。我们可以将这些形状进行共享
// 抽象的形状类
public abstract class AbstractBox {
// 获取图形的方法
public abstract String getShape();
// 显示图形颜色
public void display(String color) {
System.out.println("方块形状:" + getShape() + ", 颜色:" + color);
}
}
public class IBox extends AbstractBox{
@Override
public String getShape() {
return "I";
}
}
public class LBox extends AbstractBox{
@Override
public String getShape() {
return "L";
}
}
public class OBox extends AbstractBox{
@Override
public String getShape() {
return "O";
}
}
// 图形工厂
public class BoxFactory {
// 存储形状
private HashMap<String,AbstractBox> box;
// 创建单例的工厂对象
private static BoxFactory boxFactory = new BoxFactory();
public BoxFactory() {
this.box = new HashMap<>();
box.put("I",new IBox());
box.put("O",new OBox());
box.put("L",new LBox());
}
public static BoxFactory getBoxFactory() {
return boxFactory;
}
public AbstractBox getShape(String type) {
return box.get(type);
}
}
public class Client {
public static void main(String[] args) {
BoxFactory boxFactory = BoxFactory.getBoxFactory();
AbstractBox iShape = boxFactory.getShape("I");
iShape.display("灰色");
AbstractBox iShap2 = boxFactory.getShape("I");
iShap2.display("红色");
System.out.println(iShape == iShap2);
}
}
上边的案例急事通过一个Map来存储,如果需要什么对象直接从Map中获取即可,获取的都是同一个对象
优点
缺点
享元对象共享的关键是区分了内部状态
(Intrinsic State)和外部状态
(Extrinsic State)。
内部状态
:存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享,比如【构造方法的参数】
外部状态
:享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部,比如【 方法参数)】。随环境改变而改变的、不可以共享的状态。一个外部状态与另一个外部状态之间是相互独立的
当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多出需要使用的地方,避免大量同一对象的多次创建,降低大量内存空间的消耗。
享元模式其实是 工厂方法模式
的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。
享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式。
本章节给您介绍了结构型设计模式中的后三种,至此设计模式中的创建型
和 结构型
设计模式已经介绍完毕。接下来会继续更新最后的 行为型
设计模式。敬请期待!