门面模式(Facade Pattern)

近期参与项目研发,调接口时遇一事不爽,比如在调C方法前必须得先调A方法,成功后再调B方法拿到返回结果后,最后才可以调C方法,而且接口调用还会随需求变化而变更,比如可能还得再增加几个步骤......

 

门面模式(亦称外观模式Facade Pattern)的思想很好地解决了这一问题,当然,它的优点也不止如此。

 

场景举例

 

投递信件的过程是:首先写信的内容 -> 其次写信封 -> 把信放到信封里 -> 然后邮递

 

接口类:ILetterProcess

/**
 * @author xuanyin
 * 
 */
public interface ILetterProcess {

	/**
	 * 首先写信的内容
	 * 
	 * @param context
	 */
	void writeContext(String context);

	/**
	 * 其次写信封
	 * 
	 * @param address
	 */
	void fillEnvelope(String address);

	/**
	 * 把信放到信封里
	 */
	void letterIntoEnvelope();

	/**
	 * 然后邮递
	 */
	void sendLetter();
}

 

实现类:LetterProcess

(代码略)

 

场景类:Client

/**
 * @author xuanyin
 * 
 */
public class Client {

	public static void main(String[] args) {

		// 创建一个处理信件的过程
		ILetterProcess letterProcess = new LetterProcess();
		// 开始写信
		letterProcess.writeContext("context");
		// 开始写信封
		letterProcess.fillEnvelope("address");
		// 把信放到信封,并封装好
		letterProcess.letterIntoEnvelope();
		// 跑到邮局把信塞到邮件,投递
		letterProcess.sendLetter();
	}
}

 

上述过程,与高内聚的要求相差甚远,更不要说迪米特法则、接口隔离原则。调用方首先得知道这四个步骤,而且还不能颠倒顺序。这在面向对象的编程中是极度地不适合的,它根本就没有完成一个类所具有的单一职责。更何况,如果信件多了就非常麻烦,每封信都要这样运转一遍。

 

模式演变

 

增加一邮局类,提供信件代发业务,只需把信件的必要信息告之,就代为发送,即提供以上四个步骤的一体化服务。

 

邮局类:PostOffice

/**
 * @author xuanyin
 * 
 */
public class PostOffice {

	/**
	 * 
	 */
	private ILetterProcess letterProcess = new LetterProcess();

	/**
	 * 写信,封装,投递,一体化了
	 * 
	 * @param context
	 * @param address
	 */
	public void sendLetter(String context, String address) {

		// 帮你写信
		letterProcess.writeContext(context);
		// 写好信封
		letterProcess.fillEnvelope(address);
		// 把信放到信封中
		letterProcess.letterIntoEnvelope();
		// 邮递信件
		letterProcess.sendLetter();
	}
}

 

注意,这个新增加的邮局类就是“门面对象”

 

增加邮局类后,在场景类中调用时就简单多了

 

场景类:Client

/**
 * @author xuanyin
 * 
 */
public class Client {

	public static void main(String[] args) {

		// 现代化邮局
		PostOffice postOffice = new PostOffice();
		// 写完内容和地址后邮局一体化代发,不必关心其中过程
		postOffice.sendLetter("context", "address");
	}
}

 

可以看到,在场景类中的调用简化了很多,只需要把信息提交过去就成了,不必再知道投递过程中的所有方法和顺序了。而且这样做的可扩展性还非常好,比如在非常时期,警察需要对信件进行安全检查,就可以按如下实现。

 

警察类:Police

/**
 * @author xuanyin
 * 
 */
public class Police {

	/**
	 * 警察对信件进行检查
	 * 
	 * @param context
	 * @return true检查通过|false检查不通过
	 */
	public boolean checkLetter(ILetterProcess letterProcess) {

		return true; // or false
	}
}

 

再看下当增加检查信件步骤后,邮局类的一些变更。

 

邮局类:PostOffice

/**
 * @author xuanyin
 * 
 */
public class PostOffice {

	/**
	 * 
	 */
	private ILetterProcess letterProcess = new LetterProcess();

	/**
	 * 
	 */
	private Police police = new Police();

	/**
	 * 写信,封装,投递,一体化了
	 * 
	 * @param context
	 * @param address
	 */
	public void sendLetter(String context, String address) {

		// 帮你写信
		letterProcess.writeContext(context);
		// 写好信封
		letterProcess.fillEnvelope(address);
		// 警察检查信件
		if (police.checkLetter(letterProcess)) {
			// 把信放到信封中
			letterProcess.letterIntoEnvelope();
			// 邮递信件
			letterProcess.sendLetter();
		}
	}
}

 

增加警察检查信件这个逻辑后,并没有改动调用端的代码,也没有改变对外暴露的接口和方法,只是改变内部的处理逻辑,所以对调用端是透明的。这是一个非常棒的设计,这就是门面模式。

 

注意事项

 

一、一个接口类可以用多个门面

 

一般情况下,一个接口类只要有一个门面对象就足够了,但下列情况可以有多个:

 

1.门面对象已庞大到一定的程度:比如超过一定行数的代码,虽然都是非常简单的委托操作,也建议拆分成多个门面。拆分时可按功能拆分,比如一个数据库操作,拆分成查询门面对象,删除门面对象,更新门面对象等。

 

2.给接口类提供不同的访问路径:我的理解是,比如模块1通过门面对象A可以完整地访问所有逻辑,但模块2是受限制的,如果仍使用门面对象A,那是无法实现受限的。这时就需要新增门面对象B,提供模块2应得的方法。但在新增门面对象B时,无需重新实现。因在面向对象编程中,应尽量保持相同的代码只写一遍,避免以后修改时到处都要修改的杯具发生。如:

/**
 * 
 * @author xuanyin
 * 
 */
public class Facade2 {

	// 引用原有的门面
	private Facade1 facade1 = new Facade1();

	/**
	 * 因提供的方法在原有门面中已实现,故此处通过引用,无需重复实现
	 */
	public void methodB() {
		this.facade1.methodB();
	}
}

 

二、门面不参与接口类内的业务逻辑

 

门面对象只是提供一个访问接口的路径而已,不应该也不能参与具体的业务逻辑,否则就会产生一个倒依赖的问题:接口必须依赖门面才能被访问。如果在门面对象内有业务逻辑,则应建立一个封装类,封装完毕后提供给门面对象。

 

 

 

你可能感兴趣的:(设计模式,Facade,门面模式)