有如下属性很多的对象:
public class Product {
private final String number;
private final String price;
private final String count;
// .. 其他很多属性
public Product(String number, String price, String count) {
this.number = number;
this.price = price;
this.count = count;
}
}
那么我们每次使用它时,都自能通过有参构造器,并且要一次传入全部参数,而当下有不同的业务场景,在某些场景下这个Product的属性是我们不需要的,而针对每一种场景不同属性排列组合都要提供构造方法,在大对象下构造器数量都会是几何倍数的增长。
就可以使用建造者模式,对这个类创建一个Builder类(通常是静态内部类):
static class Builder {
private String number;
private String price;
private String count;
Builder buildNumber (String number){
this.number = number;
return this;
}
Builder buildPrice (String price){
this.price = price;
return this;
}
Builder buildCount (String count){
this.count = count;
return this;
}
Product build(){
// 做一些校验,根据不同场景赋默认值等
if (this.price == null){
// 必须赋值的属性,则抛出异常...
}
if (this.number == null){
this.number = "1";
}
if (this.count == null || "".equals(this.count)){ // 如果空,就通过计算得出
this.count = new BigDecimal(this.number).multiply(new BigDecimal(this.price)).toString();
}
return new Product(this.number,this.price,this.count);
}
}
使用时,可以只关注当前所需的属性,灵活地构造出相应对象:
Product build1 = new Product.Builder().buildPrice("3.14").buildNumber("100").buildCount("1000").build();
Product build2 = new Product.Builder().buildPrice("3.14").buildNumber("100").build();
Product build3 = new Product.Builder().buildPrice("3.14").build();
System.out.println(build1);// Product{number='100', price='3.14', count='1000'}
System.out.println(build2);// Product{number='100', price='3.14', count='314.00'}
System.out.println(build3);// Product{number='1', price='3.14', count='3.14'}
在不改变原有对象的前提下,对原来的功能进行扩展。
简单的场景可以直接继承父类,然后覆写需要扩展的方法即可
class AAA {
public AAA execute(){
//...
}
}
class BBB extends AAA {
public AAA execute(){
//... BBB做自己的扩展
super.execute();
}
}
class CCC extends AAA {
public AAA execute(){
//... CCC做自己的扩展
super.execute();
}
}
java类的继承,显得很臃肿,装饰者模式是在不改变原有类文件和使用继承的情况下,通过创建一个包装对象动态地扩展一个对象的功能,相比直接创建子类更为灵活。
class AAA {
public AAA execute(){
//...
}
}
class BBB extends AAA {
private AAA aaa;
public BBB(AAA aaa){
this.aaa = aaa;
}
public AAA execute(){
//... BBB做自己的扩展
this.aaa.execute();
}
}
class CCC extends AAA {
private AAA aaa;
public CCC(AAA aaa){
this.aaa = aaa;
}
public AAA execute(){
//... CCC做自己的扩展
this.aaa.execute();
}
}
装饰者模式下,对于我已经拿到的aaa对象,直接传到BBB的构造器中即可;但在继承下,要么强制类型转换为bbb对象,要么把属性挨个赋值再创建。
简言之,装饰者模式可以直接使用现有aaa对象,交给bbb包装。而继承适用在没有现有aaa对象前提下,创建bbb,(当然也会引发父类构造器创建出新的aaa)。装饰者更灵活。
在IO流中,字符缓冲输入流: new BufferedReader(new InputStreamReader(new FileInputStream()))
代理模式:为其他对象提供一种代理以控制对这个对象的访问。代理模式是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上也采用了代理模式。
代理模式和装饰者模式又有很多相似的地方,但代理模式强调控制访问,而装饰者模式强调扩展功能。
代码上的直观区别:
// 目标类,被代理或被装饰的类
class AAA {
public void execute(){
//...
}
}
/*
* 装饰者模式。只提供接收aaa对象的构造器,直接对客户端现有的aaa做包装。
*/
class BBB {
private AAA aaa;
public BBB(AAA aaa){
this.aaa = aaa;
}
public void execute(){
//... BBB做自己的扩展
this.aaa.execute();
}
}
/*
* 代理模式。由代理类中创建出aaa,而对客户端是无感知。客户端也不需要明确如何创建aaa。
*/
class BBB {
private AAA aaa;
public BBB(){
this.aaa = new AAA();
}
public void execute(){
//... BBB做自己的扩展
this.aaa.execute();
}
}
代理模式的访问控制主要在于对目标类的无感知访问,也就是说客户端不需要知道被代理类的具体情况,只需要知道代理类可以帮助我们完成想要的功能就对了;目标类的创建由代理类负责并对客户端隐藏。
装饰者模式,客户端是自己有一个目标类的对象的,只是这个对象不能满足我们的要求,需要对其进行扩展功能。
代理对象与真实对象的关系在编译时就确定,bbb只能代理特定的一种aaa;而装饰者在运行时构造,无论什么样的aaa对象都可以交给bbb装饰。
将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。
两种方式:组合 和 继承。
组合的方式看起来很像是将不匹配的接口"装饰"成期望的接口。
同样也是将目标类对象拿到适配器类中作为一个成员变量,并通过构造器传入现成的目标类对象完成初始化。
再通过一个方法:使用目标类的方法完成一部分逻辑,再和自己的逻辑"组合"出满足客户端的方法。
public class Adapter {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}
public void adapte() {
adaptee.target();
// ...自己的逻辑
}
}
适配器模式与装饰器模式的区别
装饰器与适配器都有一个别名叫做 包装模式(Wrapper),它们都是起到包装一个类或对象的作用,但是使用它们的目的不一样。
适配器模式的意义是要将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的。(方法签名要变)
而装饰器模式不是要改变被装饰对象的接口,而是恰恰要保持原有的接口,增强原有对象的功能,且对装饰类的使用者透明。(方法签名不变,对客户端无感知)
例如BuffedReader 中readLine() 方法对 InputStreamReader中 read()方法的适配?
就是把一系列的接口,在门面类中合并到一起作为统一对外的入口,客户端不再原本一系列接口如何使用,只需要通过门面调用即可。
其实Service中完成多个Mapper调用,组合成一个方法。就是门面模式的简单实现了。
org.apache.catalina.connector.RequestFacade
运用共享技术有效地支持大量细粒度的对象,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。
尝试复用现有的同类对象,如果未找到匹配的对象,则创建新对象,并新加入缓存。主要用于减少创建对象的数量,以减少内存占用和提高性能。其本质是缓存共享对象,降低内存消耗。
享元模式其实就是工厂模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法生成对象的,只不过享元模式中为工厂方法增加了缓存这一功能。
public class TicketFactory {
private static Map<String, ITicket> ticketPool = new ConcurrentHashMap<String, ITicket>();
public static ITicket queryTicket(String from, String to, String type) {
if (ticketPool.containsKey(key)) {
return ticketPool.get(key);
}
// 不存再则创建,并加入到缓存。(这一步就是对工厂的改进和不同之处)
ITicket ticket = new TrainTicket(from, to, type);
ticketPool.put(key, ticket);
return ticket;
}
}
享元模式实现数据库连接池:https://www.yisu.com/zixun/731423.html
经常使用的数据库连接池也使用了享元模式,有效提高了其运行的性能。
在使用数据库连接池时,由于经常使用Connection对象,其主要性能消耗在建立连接和关闭连接的时候,为了提高Connection在调用时的性能,可以将Connection对象在调用前创建好缓存起来,用的时候从缓存中取值,用完再放回去,达到资源重复利用的目的。