一、适配器模式
适配器模式通过定义一个适配器类作为两个不兼容的接口之间的桥梁,将一个类的接口转换成用户期望的另一个接口,使得两个或多个原本不兼容的接口可以基于适配器类一起工作。
适配器的实现主要分为三类:类适配器模式、对象适配器模式和接口适配器模式。
1、类适配器模式
在不改变原来接口或类结构的情况下扩展类的功能以适配不同的接口时,可以使用类的适配器模式。
(1)定义Source类:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Source { private final static Log logger = LogFactory.getLog(Source.class); public void editTextFile(){ logger.info("a text file editing"); } }
(2)定义Targetable接口:
public interface Targetable { void editTextFile(); void editWordFile(); }
(3)定义Adapter继承Source类并实现Targetable接口:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Adapter extends Source implements Targetable { private final static Log logger = LogFactory.getLog(Adapter.class); @Override public void editWordFile() { logger.info("a word file editing"); } }
(4)使用类适配器:
public class Main { public static void main(String[] args) { Targetable targetable = new Adapter(); targetable.editTextFile(); targetable.editWordFile(); } }
结果:
十二月 29, 2019 9:59:42 下午 com.jzq.sign.Adapter.Source editTextFile 信息: a text file editing 十二月 29, 2019 9:59:43 下午 com.jzq.sign.Adapter.Adapter editWordFile 信息: a word file editing
2、对象适配器模式
对象适配器模式的思路与类适配器模式基本相同,只是Adapter不再继承Source类,而是持有Source类的实例。
(1)定义Source类:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Source { private final static Log logger = LogFactory.getLog(Source.class); public void editTextFile(){ logger.info("a text file editing"); } }
(2)定义Targetable接口:
public interface Targetable { void editTextFile(); void editWordFile(); }
(3)适配器类定义:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class ObjectAdapter implements Targetable { private final static Log logger = LogFactory.getLog(ObjectAdapter.class); private Source source; public ObjectAdapter(Source source){ super(); this.source = source; } @Override public void editTextFile() { this.source.editTextFile(); } @Override public void editWordFile() { logger.info("a word file editing"); } }
(4)使用类适配器:
public class Main { public static void main(String[] args) { Source source = new Source(); Targetable targetable = new ObjectAdapter(source); targetable.editTextFile(); targetable.editWordFile(); } }
结果:
十二月 30, 2019 10:45:08 上午 com.jzq.sign.Adapter.ObjectAdapter.Source editTextFile 信息: a text file editing 十二月 30, 2019 10:45:08 上午 com.jzq.sign.Adapter.ObjectAdapter.ObjectAdapter editWordFile 信息: a word file editing
3、接口适配器模式
在不希望实现一个接口中所有方法时,可以创建一个抽象类AbstractAdapter实现所有方法,在使用时继承该抽象类按需实现方法即可。
(1)定义公共接口Sourceable:
public interface Sourceable { void editTextFile(); void editWordFile(); }
(2)定义抽象类AbstractAdapter并实现公共接口方法:
public abstract class AbstractAdapter implements Sourceable { @Override public void editTextFile(){} @Override public void editWordFile(){} }
(3)定义SourceSub1类按照需求实现editTextFile方法:
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class SourceSub1 extends AbstractAdapter { private final static Log logger = LogFactory.getLog(SourceSub1.class); @Override public void editTextFile(){ logger.info("a text file editing"); } }
(4)定义SourceSub2类按照需求实现editWordFile方法:
public class SourceSub2 extends AbstractAdapter { private final static Log logger = LogFactory.getLog(SourceSub2.class); @Override public void editWordFile(){ logger.info("a word file editing"); } }
(5)使用接口适配器:
public class Main { public static void main(String[] args) { Sourceable source1 = new SourceSub1(); Sourceable source2 = new SourceSub2(); source1.editTextFile(); source2.editWordFile(); } }
结果:
十二月 30, 2019 11:01:04 上午 com.jzq.sign.Adapter.AbstractAdapter.SourceSub1 editTextFile 信息: a text file editing 十二月 30, 2019 11:01:04 上午 com.jzq.sign.Adapter.AbstractAdapter.SourceSub2 editWordFile 信息: a word file editing
二、桥接模式
桥接模式是用于把抽象与其实现解耦,使得二者可以独立根据需求变化。
通过定义一个抽象和实现之间的桥接者来达到解耦的目的。
我们常用的JDBC和DriverManager就使用了桥接模式:
(1)定义Driver接口:
public interface Driver { void executeSQL(); }
(2)定义Driver接口的Mysql实现类MysqlDriver:
public class MysqlDriver implements Driver{ private final static Log logger = LogFactory.getLog(MysqlDriver.class); @Override public void executeSQL() { logger.info("执行Mysql数据库驱动"); } }
(3)定义Driver接口的Oracle实现类OracleDriver:
public class OracleDriver implements Driver{ private final static Log logger = LogFactory.getLog(OracleDriver.class); @Override public void executeSQL() { logger.info("执行Oracle数据库驱动"); } }
(4)定义DriverManagerBridge,用户注入不同的驱动器便能实现不同类型的数据库切换;
public class DriverManagerBridge { private Driver driver; public void execute(){ this.driver.executeSQL(); } public Driver getDriver() { return driver; } public void setDriver(Driver driver) { this.driver = driver; } }
(5)定义MyDriverBridge,实现用户自定义功能:
public class MyDriverBridge extends DriverManagerBridge { public void execute(){ getDriver().executeSQL(); } }
(6)使用桥接模式:
public class Main { public static void main(String[] args) { DriverManagerBridge driverManagerBridge = new MyDriverBridge(); //设置Mysql驱动 driverManagerBridge.setDriver(new MysqlDriver()); driverManagerBridge.execute(); //切换到Oracle驱动 driverManagerBridge.setDriver(new OracleDriver()); driverManagerBridge.execute(); } }
结果:
十二月 30, 2019 2:33:34 下午 com.jzq.sign.Bridge.MysqlDriver executeSQL 信息: 执行Mysql数据库驱动 十二月 30, 2019 2:33:34 下午 com.jzq.sign.Bridge.OracleDriver executeSQL 信息: 执行Oracle数据库驱动
三、过滤器模式
使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来
以各种标准分类Person为例:
(1)创建一个Person类,在该类上应用标准
public class Person { private String name; private String gender; private String maritalStatus; public Person(String name,String gender,String maritalStatus){ this.name = name; this.gender = gender; this.maritalStatus = maritalStatus; } public String getName() { return name; } public String getGender() { return gender; } public String getMaritalStatus() { return maritalStatus; } }
(2)定义Criteria接口:
public interface Criteria { //达到标准方法 public List<Person> meetCriteria(List<Person> persons); }
(3)创建实现了 Criteria 接口的实体类。
public class CriteriaMale implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> malePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getGender().equalsIgnoreCase("MALE")){ malePersons.add(person); } } return malePersons; } } public class CriteriaFemale implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> femalePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getGender().equalsIgnoreCase("FEMALE")){ femalePersons.add(person); } } return femalePersons; } } public class CriteriaSingle implements Criteria { @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> singlePersons = new ArrayList<Person>(); for (Person person : persons) { if(person.getMaritalStatus().equalsIgnoreCase("SINGLE")){ singlePersons.add(person); } } return singlePersons; } } public class AndCriteria implements Criteria{ private Criteria criteria; private Criteria otherCriteria; public AndCriteria(Criteria criteria, Criteria otherCriteria) { this.criteria = criteria; this.otherCriteria = otherCriteria; } @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> firstCriteriaPersons = criteria.meetCriteria(persons); return otherCriteria.meetCriteria(firstCriteriaPersons); } } public class OrCriteria implements Criteria{ private Criteria criteria; private Criteria otherCriteria; public OrCriteria(Criteria criteria, Criteria otherCriteria) { this.criteria = criteria; this.otherCriteria = otherCriteria; } @Override public List<Person> meetCriteria(List<Person> persons) { List<Person> firstCriteriaItems = criteria.meetCriteria(persons); List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons); for (Person person : otherCriteriaItems) { if(!firstCriteriaItems.contains(person)){ firstCriteriaItems.add(person); } } return firstCriteriaItems; } }
(4)使用过滤器模式:
public class Main { public static void main(String[] args) { List<Person> persons = new ArrayList<Person>(); persons.add(new Person("Robert","Male", "Single")); persons.add(new Person("John","Male", "Married")); persons.add(new Person("Laura","Female", "Married")); persons.add(new Person("Diana","Female", "Single")); persons.add(new Person("Mike","Male", "Single")); persons.add(new Person("Bobby","Male", "Single")); Criteria male = new CriteriaMale(); Criteria female = new CriteriaFemale(); Criteria single = new CriteriaSingle(); Criteria singleMale = new AndCriteria(single, male); Criteria singleOrFemale = new OrCriteria(single, female); System.out.println("Males: "); printPersons(male.meetCriteria(persons)); System.out.println(" Females: "); printPersons(female.meetCriteria(persons)); System.out.println(" Single Males: "); printPersons(singleMale.meetCriteria(persons)); System.out.println(" Single Or Females: "); printPersons(singleOrFemale.meetCriteria(persons)); } public static void printPersons(List<Person> persons){ for (Person person : persons) { System.out.println("Person : [ Name : " + person.getName() +", Gender : " + person.getGender() +", Marital Status : " + person.getMaritalStatus() +" ]"); } } }
结果:
Males:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : John, Gender : Male, Marital Status : Married ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Females:
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Single Males:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Single Or Females:
Person : [ Name : Robert, Gender : Male, Marital Status : Single ]
Person : [ Name : Diana, Gender : Female, Marital Status : Single ]
Person : [ Name : Mike, Gender : Male, Marital Status : Single ]
Person : [ Name : Bobby, Gender : Male, Marital Status : Single ]
Person : [ Name : Laura, Gender : Female, Marital Status : Married ]
四、组合模式
又叫部分整体模式,主要用于实现部分和整体操作的一致性。
组合模式通过特定的数据结构简化了部分和整体之间的关系,使客户端可以像处理单个元素一样来处理整体的数据集。
以使用 Employee 类来添加部门层次结构,并打印所有员工例:
(1)定义Employee类:
public class Employee { private String name; private String dept; private int salary; private List<Employee> subordinates; //构造函数 public Employee(String name,String dept, int sal) { this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList<Employee>(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List<Employee> getSubordinates(){ return subordinates; } public String toString(){ return ("Employee :[ Name : "+ name +", dept : "+ dept + ", salary :" + salary+" ]"); } }
(2)使用 Employee 类来创建和打印员工的层次结构。
public class Main { public static void main(String[] args) { Employee CEO = new Employee("John","CEO", 30000); Employee headSales = new Employee("Robert","Head Sales", 20000); Employee headMarketing = new Employee("Michel","Head Marketing", 20000); Employee clerk1 = new Employee("Laura","Marketing", 10000); Employee clerk2 = new Employee("Bob","Marketing", 10000); Employee salesExecutive1 = new Employee("Richard","Sales", 10000); Employee salesExecutive2 = new Employee("Rob","Sales", 10000); CEO.add(headSales); CEO.add(headMarketing); headSales.add(salesExecutive1); headSales.add(salesExecutive2); headMarketing.add(clerk1); headMarketing.add(clerk2); //打印该组织的所有员工 System.out.println(CEO); for (Employee headEmployee : CEO.getSubordinates()) { System.out.println(headEmployee); for (Employee employee : headEmployee.getSubordinates()) { System.out.println(employee); } } } }
五、装饰器模式
再无须改变原有类及类的继承关系的情况下,动态扩展一个类的功能。通过装饰者来包裹真实的对象,并动态的向对象添加或撤销功能。
(1)定义Sourceable接口:
public interface Sourceable { public void createComputer(); }
(2)定义Sourceable接口的实现类Source:
public class Source implements Sourceable { private final static Log logger = LogFactory.getLog(Source.class); @Override public void createComputer() { logger.info("create a computer by Source"); } }
(3)定义装饰器类Decorator:
public class Decorator implements Sourceable{ private Sourceable source; private final static Log logger = LogFactory.getLog(Decorator.class); public Decorator(Sourceable source){ super(); this.source=source; } @Override public void createComputer() { source.createComputer(); logger.info("make system"); } }
(4)使用装饰器模式:
public class Main { public static void main(String[] args) { Sourceable source = new Source(); Sourceable object = new Decorator(source); object.createComputer(); } }
结果:
十二月 30, 2019 12:54:50 下午 com.jzq.sign.Decorator.Source createComputer 信息: create a computer by Source 十二月 30, 2019 12:54:50 下午 com.jzq.sign.Decorator.Decorator createComputer 信息: make system
六、外观模式
又称门面模式,通过一个门面向客户端提供一个访问系统的统一接口,其主要目的是降低访问拥有多个子系统的复杂系统的难度。
外观模式将子系统中的功能抽象成一个统一的接口,客户端通过这个接口访问系统,是系统使用合起来更容易。
以汽车启动为例:
(1)定义Dashboard类、Engine类、SelfCheck类
public class Dashboard { private final static Log logger = LogFactory.getLog(Dashboard.class); public void startup(){ logger.info("开启仪表盘"); } public void shutdown(){ logger.info("关闭仪表盘"); } } public class Engine { private final static Log logger = LogFactory.getLog(Engine.class); public void startup(){ logger.info("开启发动机"); } public void shutdown(){ logger.info("关闭发动机"); } } public class SelfCheck { private final static Log logger = LogFactory.getLog(SelfCheck.class); public void startup(){ logger.info("开启自检测"); } public void shutdown(){ logger.info("关闭自检测"); } }
(2)定义门面类Starter:
public class Starter { private final static Log logger = LogFactory.getLog(Starter.class); private Dashboard dashboard; private Engine engine; private SelfCheck selfCheck; public Starter(){ this.dashboard = new Dashboard(); this.engine = new Engine(); this.selfCheck = new SelfCheck(); } public void startup(){ logger.info("开启汽车开始"); engine.startup(); dashboard.startup(); selfCheck.startup(); logger.info("开启汽车完成"); } public void shutdown(){ logger.info("关闭汽车开始"); engine.shutdown(); dashboard.shutdown(); selfCheck.shutdown(); logger.info("关闭汽车完成"); } }
(3)使用外观模式:
public class Main { public static void main(String[] args) { Starter starter = new Starter(); starter.startup(); System.out.println("=================="); starter.shutdown(); } }
结果:
十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Starter startup 信息: 开启汽车开始 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Engine startup 信息: 开启发动机 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Dashboard startup 信息: 开启仪表盘 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.SelfCheck startup 信息: 开启自检测 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Starter startup 信息: 开启汽车完成
=========================== 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Starter shutdown 信息: 关闭汽车开始 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Engine shutdown 信息: 关闭发动机 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Dashboard shutdown 信息: 关闭仪表盘 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.SelfCheck shutdown 信息: 关闭自检测 十二月 30, 2019 1:40:15 下午 com.jzq.sign.Facade.Starter shutdown 信息: 关闭汽车完成
七、享元模式
享元模式主要通过对象的复用来减少对象创建的次数和数量,以减少系统内存的使用和降低系统的负载。
在系统需要一个对象时享元模式首先在系统中查找并尝试重用现有的对象,如果未找到匹配的对象,则创建新对象并将其缓存在系统中以便下次使用。
以内存的申请和使用为例:
(1)定义Memory:
public class Memory { private int size; private boolean isused; private String id; public Memory(int size, boolean isused, String id) { this.size = size; this.isused = isused; this.id = id; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public boolean isIsused() { return isused; } public void setIsused(boolean isused) { this.isused = isused; } public String getId() { return id; } public void setId(String id) { this.id = id; } }
(2)定义MemoryFactory工厂:
import java.util.ArrayList; import java.util.List; import java.util.UUID; public class MemoryFactory { private final static Log logger = LogFactory.getLog(MemoryFactory.class); private static List<Memory> memoryList = new ArrayList<Memory>(); public static Memory getMemory(int size) { Memory memory = null; for (int i = 0; i < memoryList.size(); i++) { memory = memoryList.get(i); //如果存在和需求size一样大小并且未使用的内存块,则直接返回 if (memory.getSize() == size && !memory.isIsused()) { memory.setIsused(true); memoryList.set(i, memory); logger.info("从内存池中得到内存" + JSON.toJSONString(memory)); break; } } if (memory == null) { memory = new Memory(32, false, UUID.randomUUID().toString()); logger.info("创建一个新内存并加入内存池" + JSON.toJSONString(memory)); memoryList.add(memory); } return memory; }
public static void releaseMemory(String id){ for(int i=0;i<memoryList.size();i++){ Memory memory = memoryList.get(i); if (memory.getId().equals(id)) { memory.setIsused(false); memoryList.set(i, memory); logger.info("从内存池中释放内存id为" + id); break; } } } }
(3)使用享元模式时,直接从工厂类MemoryFactory中获取需要的数据Memory:
public class Main { public static void main(String[] args) { Memory memory = MemoryFactory.getMemory(32); //释放内存 MemoryFactory.releaseMemory(memory.getId()); //重新获得内存 MemoryFactory.getMemory(32); } }
结果:
十二月 30, 2019 8:31:33 下午 com.jzq.sign.Flyweight.MemoryFactory getMemory 信息: 创建一个新内存并加入内存池{"id":"4d571b2b-2139-4917-82aa-375fead96732","isused":false,"size":32} 十二月 30, 2019 8:31:33 下午 com.jzq.sign.Flyweight.MemoryFactory releaseMemory 信息: 从内存池中释放内存id为4d571b2b-2139-4917-82aa-375fead96732 十二月 30, 2019 8:31:33 下午 com.jzq.sign.Flyweight.MemoryFactory getMemory 信息: 从内存池中得到内存{"id":"4d571b2b-2139-4917-82aa-375fead96732","isused":true,"size":32}
八、代理模式
代理模式指为对象提供一种通过代理的方式来访问并控制该对象行为的方法。
以企业通过猎头公司招人为例:
(1)定义Company接口:
public interface Company { void findWorker(String title); }
(2)定义Company接口实现类HR:
public class HR implements Company { private final static Log logger = LogFactory.getLog(HR.class); @Override public void findWorker(String title) { logger.info("我们公司找人,职位是"+title); } }
(3)定义Proxy:
import java.util.HashMap; import java.util.Map; public class Proxy implements Company { private final static Log logger = LogFactory.getLog(Proxy.class); private HR hr; public Proxy(){ super(); this.hr = new HR(); } @Override public void findWorker(String title) { hr.findWorker(title); //通过猎头找候选人 String worker = getWorker(title); logger.info("通过代理找到一个工作者,他的名字是"+worker); } private String getWorker(String title) { //人才池 Map<String,String> workerList = new HashMap<String,String>(); workerList.put("Java","小门"); workerList.put("PHP","小六"); workerList.put("C++","小王"); return workerList.get(title); } }
(4)使用代理模式:
public class Main { public static void main(String[] args) { Company company = new Proxy(); company.findWorker("Java"); } }
结果:
十二月 30, 2019 2:07:45 下午 com.jzq.sign.Proxy.HR findWorker 信息: 我们公司找人,职位是Java 十二月 30, 2019 2:07:45 下午 com.jzq.sign.Proxy.Proxy findWorker 信息: 通过代理找到一个工作者,他的名字是小门