大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。
创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式(ProtoType)。
结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式(Bridge)、适配器模式(Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式(Flyweight)等。
行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式(Strategy)、解释器模式(Interpreter)、命令模式(Command)、观察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(Template Method)、访问者模式(Visitor)。
Spring中大量使用的以下两种设计模式:工厂模式和单例模式。Spring容器最基本的接口就是BeanFactory。而ApplicationContext是BeanFactory的子接口,大部分javaEE用这个接口就够了,也称为Spring应用上下文。
工厂模式是我们最常用的实例化对象模式了,简单讲是用工厂方法代替new操作的一种模式。
工厂模式是根据调用数据返回某个类的一个实例,此类可以是多个类的某一个类。通常,这些类满足共同的规则(接口)或父类。调用者只关心工厂生产的实例是否满足某种规范,即实现的某个接口是否可供自己正常调用(调用者仅仅使用)。该模式给对象之间作出了清晰的角色划分,降低程序的耦合。接口产生的全部实例通常用于实现相同接口,接口里定义了全部实例共同拥有的方法,这些方法在不同的实现类中实现的方式不同。从而使程序调用者无须关心方法的具体实现,降低了系统异构的代价。
还记得Hibernate的HibernateSessionFactory,Spring的BeanFactory,以及对应的子类ApplicationContext,都是工厂模式
代码展示:
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}else if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
单例模式限制了类实例的创建,但采用这种模式设计的类,可以保证仅有一个实例,并可提供访问该实例的全局访问点。J2EE应用的大量组件,都需要保证一个类只有一个实例。比如数据库引擎访问点只能有一个。
更多的时候,为了提高性能,程序应尽量减少Java 对象的创建和销毁时的开销。使用单态模式可避免Java 类被多次实例化,让相同类的全部实例共享同一内存区。
为了防止单态模式的类被多次实例化,应将类的构造器设成私有,这样就保证了只能通过静态方法获得类实例。而该静态方法则保证每次返回的实例都是同一个,这就需将该类的实例设置成类属性,由于该属性需要被静态方法访问,因此该属性应设成静态属性。
//单态模式测试类
public class SingletonTest {
//该类的一个普通属性
private int value;
//使用静态属性保存该类的一个实例
private static SingletonTest instance;
//构造器私有化,避免该类被多次实例化
private SingletonTest(){
System.out.println("正在执行构造器...");
}
//提供静态方法返回该类实例
public static SingletonTest getInstance(){
//实例化类实例前,先检查该实例是否存在
if(instance == null){
//如果不存在,则新建一个实例
instance = new SingletonTest();
}
//返回该类的成员变量:该类的实例
return instance;
}
//以下提供对普通属性value的getter和setter方法
public int getValue(){
return value;
}
public void setValue(int values){
this.value = values;
}
//main方法
public static void main(String args[]){
SingletonTest t1 = SingletonTest.getInstance();
SingletonTest t2 = SingletonTest.getInstance();
t2.setValue(9);
System.out.println(t1 == t2);
}
}
创建 HttpRequest 的过程,就是典型的构建器模式(Builder),通常会被实现成fluent 风格的 API,也有人叫它方法链。
HttpRequest request = HttpRequest.newBuilder(new URI(uri))
.header(headerAlice, valueAlice)
.headers(headerBob, value1Bob,
headerCarl, valueCarl,
headerBob, value2Bob)
.GET()
.build();
又如swagger-ui的配置,也是典型的构建器模式
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("营养探索馆-体检系统")
.description("powered by By-Health")
.termsOfServiceUrl("http://www.by-health.com/")
.contact(contact)
.version("1.0")
.build();
}
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
例如我们在new List和Map的时候,就是用的原型模式
Map<String,String> map=new HashMap<String,String>();
Map<String,String> map=new LinkedHashMap<String,String>();
List<String> list=new ArrayList<String>();
CoreBillInfo bill=new SaleOrderInfo();
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
1、系统需要使用现有的类,而此类的接口不符合系统的需要。
3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
public class Main
{
public static void main(String[] args)
{
//我有一部256g公司年会中的iphoneX
IphoneX iphone = new IphoneX();
// 在中国,用两孔插座充电
TwoPinSoketChina twoPinSoketChina = new TwoPinSoketChina();
//接上两孔插座充电
iphone.setTwoPinSoket(twoPinSoketChina);
//充电
iphone.charge();
// 然后坐飞机去美国旅游,美国某旅馆的墙上有只有一个三孔插座
ThreePinSoketAmerica threePinSoketAmerica = new ThreePinSoketAmerica();
// 幸好我有美国适配器,一头插到三孔插座,另一头转换成二孔插座,就可以给我的荣耀手机充电
TwoPinAdapter twopinAdapter = new TwoPinAdapter(threePinSoketAmerica);
// 在美国,通过美国适配器,用三空插座充电
iphone.setTwoPinSoket(twopinAdapter);
//充电
iphone.charge();
}
}