设计模式——门面、适配器和注册器模式

#阅读# #设计模式#


前言


在做聚合搜索平台时用到了门面模式、适配器模式和注册器模式,正巧抽到了《大话设计模式》(Java 溢彩加强版),就借此机会总结一下。


门面模式


定义


门面模式( Facade,又称外观模式),为子系统中的一组接口提供一个一致的界面,此该模式定义了一个高级接口,这个接口使得这一子系统更加容易使用。


结构图



  1. Facade:外观类
  2. 知道有哪些子类系统负责处理请求(引用子系统对象)
  3. 将客户的请求代理给适当的子系统对象(在方法中调用子系统对象的方法)
  4. SubSystem Classes:子系统类集合
  5. 实现子系统的功能
  6. 子系统中没有 Facade 的任何信息,即没有对 Facade 的引用


示例


有几个股票类:


股票 1


public class Stock1 {
public void buy() {
System.out.println("股票1买入");
}
public void sell() {
System.out.println("股票1卖出");
}
}


股票 2


public class Stock2 {
public void buy() {
System.out.println("股票2买入");
}
public void sell() {
System.out.println("股票2卖出");
}
}


国债 1


public class NationalDept1 {
public void buy() {
System.out.println("国债1买入");
}
public void sell() {
System.out.println("国债1卖出");
}
}


房地产 1


public class Realty1 {
public void buy() {
System.out.println("房地产1买入");
}
public void sell() {
System.out.println("房地产1卖出");
}
}


用户需要了解各股票、国债、房产情况,并参与到其中的买和卖,耦合性过高


Stock1 stock1 = new Stock1();
Stock2 stock2 = new Stock2();
NationalDept1 nd1 = new NationalDept1();
Realty1 rt1 = new Realty1();
// 了解各股票信息,并买入
stock1.buy();
stock2.buy();
nd1.buy();
rt1.buy();
// 认为合适的时机卖出
stock1.sell();
stock2.sell();
nd1.sell();
rt1.sell();


可以引入基金类,用来了解所有股票或其他投资方式的信息,进行组合,供外界调用


public class Fund {
Stock1 stock1;
Stock2 stock2;
NationalDept1 nd1;
Realty1 rt1;

public Fund() {
stock1 = new Stock1();
stock2 = new Stock2();
nd1 = new NationalDept1();
rt1 = new Realty1();
}

public void buyFund() {
stock1.buy();
stock2.buy();
nd1.buy();
rt1.buy();
}

public void sellFund() {
stock1.sell();
stock2.sell();
nd1.sell();
rt1.sell();
}
}


客户端调用:


Fund fund1 = new Fund();
// 基金购买
fund1.buyFund();
// 基金赎回
fund1.sellFund();


这样,客户可以不了解股票、国债、房地产等投资信息,甚至对它们一无所知,只需要关心基金的上涨和下跌即可,由基金经理人与多种股票等投资产品打交道。


适用场景


  1. 在设计初期阶段,应该有意识地将不同的两个层分离。比如经典的三层架构,需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层之间建立外观 Facade,降低之间的耦合。
  2. 在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,可以增加一个外观 Facade 来提供一个简单的接口,减少它们之间的依赖。
  3. 在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但它包含了非常重要的功能,新的需求开发必须依赖它,可以为新系统开发一个外观 Facade 类,与遗留代码交互所有复杂的工作,并提供比较清晰简单的接口供新系统调用


适配器模式


定义


适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。


结构图



  1. Target:客户期待的接口,目标可以是具体或抽象的类,也可以是接口
  2. Adapter:通过在内部包装一个 Adaptee 对象,把源接口转换为目标接口
  3. Adaptee:需要适配的类


示例


有个球员抽象类,包含进攻和防守方法,前锋、中锋和后卫都继承该类


球员类


public abstract class Player {
protected String name;
public Player(String name) {
this.name = name;
}

public abstract void attack();
public abstract void defense();
}


前锋


public class Forwards extends Player{
public Forwards(String name) {
super(name);
}

@Override
public void attack() {
System.out.println("前锋 " + this.name + " 进攻");
}

@Override
public void defense() {
System.out.println("前锋 " + this.name + " 防守");
}
}


中锋


public class Center extends Player{
public Center(String name) {
super(name);
}

@Override
public void attack() {
System.out.println("中锋 " + this.name + " 进攻");
}

@Override
public void defense() {
System.out.println("中锋 " + this.name + " 防守");
}
}


后卫


public class Guards extends Player{
public Guards(String name) {
super(name);
}

@Override
public void attack() {
System.out.println("后卫 " + this.name + " 进攻");
}

@Override
public void defense() {
System.out.println("后卫 " + this.name + " 防守");
}
}


客户端代码:


Player forwards = new Forwards("巴蒂尔");
Player guards = new Guards("麦克格雷迪");
Player center = new Center("奥尼尔");

forwards.attack();
center.attack();
guards.defense();
center.defense();


这时来了个外籍中锋,但他听不懂 attack 和 defense 指令


public class ForeignCenter {
private String name;

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void 进攻() {
System.out.println("外籍中锋 " + this.name + " 进攻");
}

public void 防守() {
System.out.println("外籍中锋 " + this.name + " 防守");
}
}


需要有一个翻译者来”适配“


public class Translator extends Player{

private ForeignCenter foreignCenter = new ForeignCenter();

public Translator(String name) {
super(name);
foreignCenter.setName(name);
}

@Override
public void attack() {
foreignCenter.进攻();
}

@Override
public void defense() {
foreignCenter.防守();
}
}


客户端代码:


Player center = new Translator("姚明");

center.attack();
center.defense();



适用场景


  1. 当希望使用一个已经存在的类,但是接口(方法)又与要求不一致的情况,可以考虑运用适配器模式。
  2. 要在双方都不太容易修改的时候再使用适配器模式进行适配(通常在软件开发后期或维护期考虑),前期如果发现有类似的功能类的接口不同时,应该先考虑通过重构统一接口。
  3. 使用第三方开发组件,并且这个组件的接口与我们自己的系统接口不相同时,可以通过适配器模式来解决接口不同的问题


注册器模式


定义


注册器模式(Registry),是一种创建对象的设计模式,它将所有需要使用的对象都注册到一个中央注册器中,并提供统一的接口来访问这些对象。注册器模式可以提高系统的灵活性和可扩展性,同时也便于进行统一的配置和管理。


结构图



  1. Registry:注册器,通常包含向注册表中添加、删除和查找对象等操作方法,注册表可以采用不同的数据结构来存储,如哈希表、列表或树等
  2. Product:抽象产品,定义所有具体产品类的基本特征和行为
  3. ProductA,ProductB:实现抽象产品接口的具体类


示例


定义一个抽象产品接口,并实现一些具体产品


Product


public interface Product {
public String getName();
}


ProductA


public class ProductA implements Product {
@Override
public String getName() {
return "Product A";
}
}


ProductB


public class ProductB implements Product {
@Override
public String getName() {
return "Product B";
}
}


创建注册器:


public class Registry {
private static Map<String, Product> products = new HashMap<>();

public static void registerProduct(String name, Product product) {
products.put(name, product);
}

public static Product getProduct(String name) {
return products.get(name);
}
}


客户端调用:


// 注册产品
Registry.registerProduct("Product A", new ProductA());
Registry.registerProduct("Product B", new ProductB());

// 获取产品
Product product = Registry.getProduct("Product A");
System.out.println(product.getName());


适用场景


  1. 当需要支持对象的注册、查找和管理时,可以使用注册器模式来提高系统灵活性和可扩展性。例如,数据库连接池、事件监听器机制等。
  2. 当需要解耦合作组件之间的关系,并且允许在运行时添加、删除和修改组件时,可以使用注册器模式。应用场景包括插件式架构、IoC 容器等。
  3. 当需要全局访问某个对象时,可以使用注册器模式。在这种情况下,通过将对象注册到全局的注册器中,可以在任何时候使用已经注册的对象,方便对象的访问。
  4. 当需要在不同的组件之间建立一致的接口并进行交互时,可以使用注册器模式。例如,在插件式架构中,不同的插件提供不同的功能,为了统一这些插件的功能接口,可以将它们注册到同一个注册器中,并通过注册器来管理和调用这些插件。
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
Gianhing
下载 APP