设计模式实践
文章目录
23中设计模式(Java实现)
1.单例模式
饿汉模式
//饿汉模式,线程安全
public class SingletonMode {
//默认的静态实现,初始化一个实现
private static final SingletonMode singletonMode=new SingletonMode();
//使用一个私有的无参构造,封死别人用new创建
private SingletonMode(){
}
//对外开放一个接口来使用该单利
public static SingletonMode getSingletonMode(){
return singletonMode;
}
//单例里面的一些静态方法
public static void doSomething(){
System.out.println("饿汉模式:这里可以搞一些事情");
}
}
懒汉模式
//懒汉模式,线程不安全,多线程下需要加synchronized来控制线程安全
class Singleton {
private static Singleton singleton = null;
//限制产生多个对象
private Singleton(){
}
//通过该方法获得实例对象,如果想要线程安全,此函数需要加synchronized来实现
public static Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
//单例里面的一些静态方法
public static void doSomething(){
System.out.println("懒汉模式:这里可以搞一些事情");
}
}
2.工厂模式
工厂模式通用代码
package design;
/**
* @title: FactoryMode 工厂方法
* @projectName JavaTest
* @Date: 2021/5/21 14:23
* @Author: lei.yu-esint
* @Description:
*/
//1.抽象产品类
abstract class Product {
//产品类的公共方法
public void method1(){
System.out.println("产品类的公共方法业务逻辑...");
}
//产品方法2
public abstract void method2();
}
//1-1 具体产品类
class Product1 extends Product{
public void method2(){
System.out.println("我是产品: Product1 ");
}
}
//1-2 具体产品类
class Product2 extends Product{
public void method2(){
System.out.println("我是产品: Product2 ");
}
}
//2.抽象工厂类
public abstract class FactoryMode {
/**
* 创建一个产品对象,其输入参数类型可以自行设置
* 通常为String Enum Class 等, 当然也可以为空
*/
public abstract <T extends Product> T creatProduct(Class<T> c);
}
//2-1.具体工厂类
class FactoryModeSx extends FactoryMode{
public <T extends Product> T creatProduct(Class<T> c){
Product product=null;
try{
product = (Product)Class.forName(c.getName()).newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T)product;
};
}
//具体业务的场景类
class Client{
public static void main(String[] args){
FactoryMode factoryMode=new FactoryModeSx();
Product product=factoryMode.creatProduct(Product1.class);
//剩下的就是自己的业务了...
}
}
简单工厂模式
//简单工厂模式*************************************************************
//对于简单工厂类,我们需要在通用代码的基础上做两件事:1.取消抽象工厂 2.将工厂类中的创建产品方法静态化
class FactoryModeSxJy{
public static <T extends Product> T creatProduct(Class<T> c){
Product product=null;
try{
product = (Product)Class.forName(c.getName()).newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T)product;
};
}
多工厂模式
//注意:抽象方法中已经不再需要传入类参数了,因为实现了该抽象方法后每个工厂实现方法只做一件事情
abstract class FactoryModeDuo {
/**
* 创建一个产品对象
* 通常为String Enum Class 等, 当然也可以为空
*/
public abstract <T extends Product> T creatProduct();
}
//然后就是各种工厂
class FactoryModeDuo1 {
/**
* 创建一个产品对象
*/
public <T extends Product> T creatProduct(){
Product product=new Product1();
return (T) product;
};
}
class FactoryModeDuo2 {
/**
* 创建一个产品对象
*/
public <T extends Product> T creatProduct(){
Product product=new Product2();
return (T) product;
};
}
3.抽象工厂
通用代码
//3.抽象工厂+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//抽象产品类A****
abstract class AbstractProductA{
//每个产品共有的方法
public void shareMethod(){}
//每个产品相同方法,不同实现
public abstract void doSomething();
}
//产品A1的实现类
class ProductA1 extends AbstractProductA{
public void doSomething(){
System.out.println("产品A1的实现方法");
}
}
//产品A2的实现类
class ProductA2 extends AbstractProductA{
public void doSomething(){
System.out.println("产品A2的实现方法");
}
}
//抽象产品类B****
abstract class AbstractProductB{
//每个产品共有的方法
public void shareMethod(){}
//每个产品相同方法,不同实现
public abstract void doSomething();
}
//产品A1的实现类
class ProductB1 extends AbstractProductB{
public void doSomething(){
System.out.println("产品B1的实现方法");
}
}
//产品A2的实现类
class ProductB2 extends AbstractProductB{
public void doSomething(){
System.out.println("产品B2的实现方法");
}
}
//抽象工厂类
abstract class AbstractCreator {
//创建A产品家族
public abstract AbstractProductA createProductA();
//创建B产品家族
public abstract AbstractProductB createProductB();
}
//产品等级1的实现类
class Creator1 extends AbstractCreator {
//只生产产品等级为1的A产品
public AbstractProductA createProductA() {
return new ProductA1();
}
//只生产产品等级为1的B产品
public AbstractProductB createProductB() {
return new ProductB1();
}
}
//产品等级2的实现类
class Creator2 extends AbstractCreator {
//只生产产品等级为2的A产品
public AbstractProductA createProductA() {
return new ProductA2();
}
//只生产产品等级为2的B产品
public AbstractProductB createProductB() {
return new ProductB2();
}
}
//场景类
class productClient {
public static void main(String[] args) {
//定义出两个工厂
AbstractCreator creator1 = new Creator1();
AbstractCreator creator2 = new Creator2();
//产生A1对象
AbstractProductA a1 = creator1.createProductA();
//产生A2对象
AbstractProductA a2 = creator2.createProductA();
//产生B1对象
AbstractProductB b1 = creator1.createProductB();
//产生B2对象
AbstractProductB b2 = creator2.createProductB();
/*
* 然后在这里就可以为所欲为了...
*/
}
}
4.责任链模式
责任链模式定义如下:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)
责任链模式的优点:
责任链模式非常显著的优点是将请求和处理分开。请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌(例如在J2EE项目开发中,可以剥离出无状态Bean由责任链处理),两者解耦,提高系统的灵活性。
责任链模式的缺点:
责任链有两个非常显著的缺点:一是性能问题,每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题。二是调试不很方便,特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。
责任链模式的注意事项:
链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。
通用代码
/**
* 责任链通用代码
* @title: ResponsibilityChainMode
* @projectName JavaTest
* @Date: 2021/6/4 9:48
* @Author: lei.yu-esint
* @Description:
*/
//**************场景实现类****************
public class ResponsibilityChainMode {
public static void main(String[] args) {
//声明所有的处理节点
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
//设置链中的阶段顺序1-->2-->3
handler1.setNext(handler2);
handler2.setNext(handler3);
//提交请求,返回结果
Response response = handler1.handlerMessage(new Request());
}
}
//**************抽象处理者****************
abstract class Handler{
private Handler nextHandler;
//每个处理者都必须对请求做出处理
public final Response handlerMessage(Request request){
Response response = null;
//判断是否是自己的处理级别
if(this.getHandlerLevel().equals(request.getRequestLevel())){
response = this.echo(request);
}else{ //不属于自己的处理级别
//判断是否有下一个处理者
if(this.nextHandler != null){
response = this.nextHandler.handlerMessage(request);
}else{
//没有适当的处理者,业务自行处理
}
}
return response;
}
//设置下一个处理者是谁
public void setNext(Handler _handler){
this.nextHandler = _handler;
}
//每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
//每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
//**************处理者1****************
class ConcreteHandler1 extends Handler {
//定义自己的处理逻辑
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
//**************处理者2****************
class ConcreteHandler2 extends Handler {
//定义自己的处理逻辑
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
//**************处理者3****************
class ConcreteHandler3 extends Handler {
//定义自己的处理逻辑
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
//定义一个请求和处理等级
class Level {
}
class Request {
//请求的等级
public Level getRequestLevel(){
return null;
}
}
class Response {
//处理者返回的数据
}
5.策略模式
策略模式的定义
策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern)。其定义如下:
Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)
这个定义是非常明确、清晰的,“定义一组算法”,看看我们的三个计谋是不是三个算法?“将每个算法都封装起来”,封装类Context不就是这个作用吗?“使它们可以互换”当然可以互换了,都实现是相同的接口,那当然可以相互转化了。我们看看策略模式的通用类图
策略模式使用的就是面向对象的继承和多态机制,非常容易理解和掌握,我们再来看看策略模式的三个角色:
● Context封装角色
它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
● Strategy抽象策略角色
策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。各位看官可能要问了,类图中的AlgorithmInterface是什么意思,嘿嘿,algorithm是“运算法则”的意思,结合起来意思就明白了吧。
● ConcreteStrategy具体策略角色
实现抽象策略中的操作,该类含有具体的算法。
我们再来看策略模式的通用源码,非常简单。先看抽象策略角色,它是一个非常普通的接口,在我们的项目中就是一个普通得不能再普通的接口了,定义一个或多个具体的算法
代码示例
//抽象的策略角色
public interface Strategy {
//策略模式的运算法则
public void doSomething();
}
//具体策略角色
public class ConcreteStrategy1 implements Strategy {
public void doSomething() {
System.out.println("具体策略1的运算法则");
}
}
public class ConcreteStrategy2 implements Strategy {
public void doSomething() {
System.out.println("具体策略2的运算法则");
}
}
//封装角色
public class Context {
//抽象策略
private Strategy strategy = null;
//构造函数设置具体策略
public Context(Strategy _strategy){
this.strategy = _strategy;
}
//封装后的策略方法
public void doAnythinig(){
this.strategy.doSomething();
}
}
//高层模块
public class Client {
public static void main(String[] args) {
//声明一个具体的策略
Strategy strategy = new ConcreteStrategy1();
//声明上下文对象
Context context = new Context(strategy);
//执行封装后的方法
context.doAnythinig();
}
}
策略模式就是这么简单,偷着乐吧,它就是采用了面向对象的继承和多态机制,其他没什么玄机。想想看,你真实的业务环境有这么简单吗?一个类实现多个接口很正常,你要有火眼金睛看清楚哪个接口是抽象策略接口,哪些是和策略模式没有任何关系,这就是你作为系统分析师的价值所在。
策略模式的优点
● 算法可以自由切换
这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对其进行封装,保证对外提供“可自由切换”的策略。
● 避免使用多重条件判断
如果没有策略模式,我们想想看会是什么样子?一个策略家族有5个策略算法,一会要使用A策略,一会要使用B策略,怎么设计呢?使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。
● 扩展性良好
这甚至都不用说是它的优点,因为它太明显了。在现有的系统中增加一个策略太容易了,只要实现接口就可以了,其他都不用修改,类似于一个可反复拆卸的插件,这大大地符合了OCP原则。
18.3.2 策略模式的缺点
● 策略类数量增多
每一个策略都是一个类,复用的可能性很小,类数量增多。
● 所有的策略类都需要对外暴露
上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。
18.3.3 策略模式的使用场景
● 多个类只有在算法或行为上稍有不同的场景。
● 算法需要自由切换的场景。
例如,算法的选择是由使用者决定的,或者算法始终在进化,特别是一些站在技术前沿的行业,连业务专家都无法给你保证这样的系统规则能够存在多长时间,在这种情况下策略模式是你最好的助手。
● 需要屏蔽算法规则的场景。
现在的科技发展得很快,人脑的记忆是有限的(就目前来说是有限的),太多的算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果,万事大吉。
18.3.4 策略模式的注意事项
如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋,谁都不想接。
6.代理模式
代理模式的定义
代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:
Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)
代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应用中,代理模式可以提供非常好的访问控制。在一些著名开源软件中也经常见到它的身影,如Struts2的Form元素映射就采用了代理模式(准确地说是动态代理模式)。我们先看一下类图中的三个角色的定义:
● Subject抽象主题角色
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
● RealSubject具体主题角色
也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
● Proxy代理主题角色
也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
通用代码
我们首先来看Subject抽象主题类的通用源码:
//抽象主题类
public interface Subject {
//定义一个方法
public void request();
}
//在接口中我们定义了一个方法request来作为方法的代表,RealSubject对它进行实现,如代码清单12-7所示。
//真实主题类
public class RealSubject implements Subject {
//实现方法
public void request() {
//业务逻辑处理
}
}
RealSubject是一个正常的业务实现类,代理模式的核心就在代理类上,如代码清单:
// 代理类
public class Proxy implements Subject {
//要代理哪个实现类
private Subject subject = null;
//默认被代理者
public Proxy(){
this.subject = new Proxy();
}
//通过构造函数传递代理者
public Proxy(Object...objects ){
}
//实现接口中定义的方法
public void request() {
this.before();
this.subject.request();
this.after();
}
//预处理
private void before(){
//do something
}
//善后处理
private void after(){
//do something
}
}
看到这里,大家别惊讶,为什么会出现before和after方法,继续看下去,这是一个“引子”,能够引出一个崭新的编程模式。
一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的。当然,最简单的情况就是一个主题类和一个代理类,这是最简洁的代理模式。在通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块来决定,也就是在代理类的构造函数中传递被代理者,例如我们可以在代理类Proxy中增加如代码清单12-9所示的构造函数。
代码清单:
//代理的构造函数
public Proxy(Subject _subject){
this.subject = _subject;
}
你要代理谁就产生该代理的实例,然后把被代理者传递进来,该模式在实际的项目应用中比较广泛。
12.3 代理模式的应用
12.3.1 代理模式的优点
● 职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰。
● 高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
● 智能化
这在我们以上的讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化有兴趣的读者也可以看看Struts是如何把表单元素映射到对象上的。
12.3.2 代理模式的使用场景
我相信第一次接触到代理模式的读者肯定很郁闷,为什么要用代理呀?想想现实世界吧,打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的答辩就成,其他的比如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。代理模式的使用场景非常多,大家可以看看Spring AOP,这是一个非常典型的动态代理。