Java 中几种常用设计模式
Java 中一般认为有23种设计模式,当然暂时不需要所有的都会,但是其中常见的几种设计模式应该去掌握。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1. 单例模式
所谓的单例设计指的是一个类只允许产生一个实例化对象。
最好理解的一种设计模式,分为懒汉式和饿汉式。
1.饿汉式
——构造方法私有化,外部无法产生新的实例化对象,只能通过static方法取得实例化对象
class Singleton {
/**
* 在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
*/
private static Singleton instance = new Singleton();
/**
* private 声明构造
*/
private Singleton() { }
/**
* 返回对象实例
*/
public static Singleton getInstance() {
return instance;
} public void print() {
System.out.println("Hello Singleton...");
}
}
2.懒汉式
——当第一次去使用Singleton对象的时候才会为其产生实例化对象的操作
class Singleton { /**
* 声明变量
*/
private static volatile Singleton singleton = null; /**
* 私有构造方法
*/
private Singleton() { } /**
* 提供对外方法
* @return
*/
public static Singleton getInstance() {
// 还未实例化
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
public void print() {
System.out.println("Hello World");
}
}
当多个线程并发执行 getInstance 方法时,懒汉式会存在线程安全问题,所以用到了 synchronized 来实现线程的同步,当一个线程获得锁的时候其他线程就只能在外等待其执行完毕。
而饿汉式则不存在线程安全的问题。
2. 工厂设计模式
工厂模式分为工厂方法模式和抽象工厂模式。
工厂方法模式
工厂方法模式:
1. 工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
2. 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
3. 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
1. 普通工厂模式
建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
interface Sender {
void Send();
} class MailSender implements Sender { @Override
public void Send() {
System.out.println("This is mail sender...");
}
} class SmsSender implements Sender { @Override
public void Send() {
System.out.println("This is sms sender...");
}
} public class FactoryPattern {
public static void main(String[] args) {
Sender sender = produce("mail");
sender.Send();
}
public static Sender produce(String str) {
if ("mail".equals(str)) {
return new MailSender();
} else if ("sms".equals(str)) {
return new SmsSender();
} else {
System.out.println("输入错误...");
return null;
}
}
}
2. 多个工厂方法模式
该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
interface Sender {
void Send();
} class MailSender implements Sender { @Override
public void Send() {
System.out.println("This is mail sender...");
}
} class SmsSender implements Sender { @Override
public void Send() {
System.out.println("This is sms sender...");
}
} class SendFactory {
public Sender produceMail() {
return new MailSender();
} public Sender produceSms() {
return new SmsSender();
}
} public class FactoryPattern {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
3. 静态工厂方法模式
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
interface Sender {
void Send();
} class MailSender implements Sender { @Override
public void Send() {
System.out.println("This is mail sender...");
}
} class SmsSender implements Sender { @Override
public void Send() {
System.out.println("This is sms sender...");
}
} class SendFactory {
public static Sender produceMail() {
return new MailSender();
} public static Sender produceSms() {
return new SmsSender();
}
} public class FactoryPattern {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}
抽象工厂模式
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?
那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
interface Provider {
Sender produce();
} interface Sender {
void Send();
} class MailSender implements Sender { public void Send() {
System.out.println("This is mail sender...");
}
} class SmsSender implements Sender { public void Send() {
System.out.println("This is sms sender...");
}
} class SendMailFactory implements Provider { public Sender produce() {
return new MailSender();
}
} class SendSmsFactory implements Provider { public Sender produce() {
return new SmsSender();
}
} public class FactoryPattern {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
3. 建造者模式
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
import java.util.ArrayList;
import java.util.List; /**
* @Author: LiuWang
* @Created: 2018/8/6 17:47
*/ abstract class Builder {
/**
* 第一步:装CPU
*/
public abstract void buildCPU(); /**
* 第二步:装主板
*/
public abstract void buildMainBoard(); /**
* 第三步:装硬盘
*/
public abstract void buildHD(); /**
* 获得组装好的电脑
* @return
*/
public abstract Computer getComputer();
} /**
* 装机人员装机
*/
class Director {
public void Construct(Builder builder) {
builder.buildCPU();
builder.buildMainBoard();
builder.buildHD();
}
} /**
* 具体的装机人员
*/
class ConcreteBuilder extends Builder { Computer computer = new Computer(); @Override
public void buildCPU() {
computer.Add("装CPU");
} @Override
public void buildMainBoard() {
computer.Add("装主板");
} @Override
public void buildHD() {
computer.Add("装硬盘");
} @Override
public Computer getComputer() {
return computer;
}
} class Computer { /**
* 电脑组件集合
*/
private List<String> parts = new ArrayList<String>(); public void Add(String part) {
parts.add(part);
} public void print() {
for (int i = 0; i < parts.size(); i++) {
System.out.println("组件:" + parts.get(i) + "装好了...");
}
System.out.println("电脑组装完毕...");
}
} public class BuilderPattern { public static void main(String[] args) {
Director director = new Director();
Builder builder = new ConcreteBuilder();
director.Construct(builder);
Computer computer = builder.getComputer();
computer.print();
}
}
4. 适配器设计模式
适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题。
主要分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
1. 类的适配器模式:
class Source {
public void method1() {
System.out.println("This is original method...");
}
} interface Targetable { /**
* 与原类中的方法相同
*/
public void method1(); /**
* 新类的方法
*/
public void method2();
} class Adapter extends Source implements Targetable { @Override
public void method2() {
System.out.println("This is the targetable method...");
}
} public class AdapterPattern {
public static void main(String[] args) {
Targetable targetable = new Adapter();
targetable.method1();
targetable.method2();
}
}
2. 对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter 类作修改,这次不继承Source 类,而是持有Source 类的实例,以达到解决兼容性的问题。
class Source {
public void method1() {
System.out.println("This is original method...");
}
} interface Targetable { /**
* 与原类中的方法相同
*/
public void method1(); /**
* 新类的方法
*/
public void method2();
} class Wrapper implements Targetable { private Source source; public Wrapper(Source source) {
super();
this.source = source;
} @Override
public void method1() {
source.method1();
} @Override
public void method2() {
System.out.println("This is the targetable method...");
}
} public class AdapterPattern {
public static void main(String[] args) {
Source source = new Source();
Targetable targetable = new Wrapper(source);
targetable.method1();
targetable.method2();
}
}
3. 接口的适配器模式
接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
/**
* 定义端口接口,提供通信服务
*/
interface Port {
/**
* 远程SSH端口为22
*/
void SSH(); /**
* 网络端口为80
*/
void NET(); /**
* Tomcat容器端口为8080
*/
void Tomcat(); /**
* MySQL数据库端口为3306
*/
void MySQL();
} /**
* 定义抽象类实现端口接口,但是什么事情都不做
*/
abstract class Wrapper implements Port {
@Override
public void SSH() { } @Override
public void NET() { } @Override
public void Tomcat() { } @Override
public void MySQL() { }
} /**
* 提供聊天服务
* 需要网络功能
*/
class Chat extends Wrapper {
@Override
public void NET() {
System.out.println("Hello World...");
}
} /**
* 网站服务器
* 需要Tomcat容器,Mysql数据库,网络服务,远程服务
*/
class Server extends Wrapper {
@Override
public void SSH() {
System.out.println("Connect success...");
} @Override
public void NET() {
System.out.println("WWW...");
} @Override
public void Tomcat() {
System.out.println("Tomcat is running...");
} @Override
public void MySQL() {
System.out.println("MySQL is running...");
}
} public class AdapterPattern { private static Port chatPort = new Chat();
private static Port serverPort = new Server(); public static void main(String[] args) {
// 聊天服务
chatPort.NET(); // 服务器
serverPort.SSH();
serverPort.NET();
serverPort.Tomcat();
serverPort.MySQL();
}
}
5. 装饰模式
顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
interface Shape {
void draw();
} /**
* 实现接口的实体类
*/
class Rectangle implements Shape { @Override
public void draw() {
System.out.println("Shape: Rectangle...");
}
} class Circle implements Shape { @Override
public void draw() {
System.out.println("Shape: Circle...");
}
} /**
* 创建实现了 Shape 接口的抽象装饰类。
*/
abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape; public ShapeDecorator(Shape decoratedShape) {
this.decoratedShape = decoratedShape;
} @Override
public void draw() {
decoratedShape.draw();
}
} /**
* 创建扩展自 ShapeDecorator 类的实体装饰类。
*/
class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
} @Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
} private void setRedBorder(Shape decoratedShape) {
System.out.println("Border Color: Red");
}
} /**
* 使用 RedShapeDecorator 来装饰 Shape 对象。
*/
public class DecoratorPattern {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw(); System.out.println("\nCircle of red border");
redCircle.draw(); System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
6. 策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
/**
* 抽象算法的策略类,定义所有支持的算法的公共接口
*/
abstract class Strategy {
/**
* 算法方法
*/
public abstract void AlgorithmInterface();
} /**
* 具体算法A
*/
class ConcreteStrategyA extends Strategy {
//算法A实现方法
@Override
public void AlgorithmInterface() {
System.out.println("算法A的实现");
}
} /**
* 具体算法B
*/
class ConcreteStrategyB extends Strategy {
/**
* 算法B实现方法
*/
@Override
public void AlgorithmInterface() {
System.out.println("算法B的实现");
}
} /**
* 具体算法C
*/
class ConcreteStrategyC extends Strategy {
@Override
public void AlgorithmInterface() {
System.out.println("算法C的实现");
}
} /**
* 上下文,维护一个对策略类对象的引用
*/
class Context {
Strategy strategy; public Context(Strategy strategy) {
this.strategy = strategy;
} public void contextInterface(){
strategy.AlgorithmInterface();
}
} /**
* 客户端代码:实现不同的策略
*/
public class StrategyPattern {
public static void main(String[] args) { Context context; context = new Context(new ConcreteStrategyA());
context.contextInterface(); context = new Context(new ConcreteStrategyB());
context.contextInterface(); context = new Context(new ConcreteStrategyC());
context.contextInterface();
}
}
7. 代理模式
代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。
通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。
1. 静态代理
静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。
/**
* 代理类和被代理类共同实现的接口
*/
interface IService { void service();
} /**
* 被代理类
*/
class Service implements IService{ @Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
} /**
* 代理类
*/
class ProxyService implements IService{
/**
* 持有被代理对象的引用
*/
private IService service; /**
* 默认代理Service类
*/
public ProxyService() {
this.service = new Service();
} /**
* 也可以代理实现相同接口的其他类
* @param service
*/
public ProxyService(IService service) {
this.service = service;
} @Override
public void service() {
System.out.println("开始执行service()方法");
service.service();
System.out.println("service()方法执行完毕");
}
} //测试类
public class ProxyPattern { public static void main(String[] args) {
IService service = new Service();
//传入被代理类的对象
ProxyService proxyService = new ProxyService(service);
proxyService.service();
}
}
2. 动态代理
JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。
动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。
创建动态代理对象的步骤:
1. 指明一系列的接口来创建一个代理对象
2. 创建一个调用处理器(InvocationHandler)对象
3. 将这个代理指定为某个其他对象的代理对象
4. 在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* 代理类和被代理类共同实现的接口
*/
interface IService {
void service();
} class Service implements IService{ @Override
public void service() {
System.out.println("被代理对象执行相关操作");
}
} class ServiceInvocationHandler implements InvocationHandler { /**
* 被代理的对象
*/
private Object srcObject; public ServiceInvocationHandler(Object srcObject) {
this.srcObject = srcObject;
} @Override
public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
System.out.println("开始执行"+method.getName()+"方法");
//执行原对象的相关操作,容易忘记
Object returnObj = method.invoke(srcObject,args);
System.out.println(method.getName()+"方法执行完毕");
return returnObj;
}
} public class ProxyPattern {
public static void main(String[] args) {
IService service = new Service();
Class<? extends IService> clazz = service.getClass(); IService proxyService = (IService) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(), new ServiceInvocationHandler(service));
proxyService.service();
}
}
Java 中几种常用设计模式的更多相关文章
- Java中几种常用数据类型之间转换的方法
Java中几种常用的数据类型之间转换方法: 1. short-->int 转换 exp: short shortvar=0; int intvar=0; shortvar= (short) in ...
- java 中几种常用数据结构
Java中有几种常用的数据结构,主要分为Collection和map两个主要接口(接口只提供方法,并不提供实现),而程序中最终使用的数据结构是继承自这些接口的数据结构类. 一.几个常用类的区别 1.A ...
- Java中23种经典设计模式详解
Java中23种设计模式目录1. 设计模式 31.1 创建型模式 41.1.1 工厂方法 41.1.2 抽象工厂 61.1.3 建造者模式 101.1.4 单态模式 131.1.5 原型模式 151. ...
- Java的几种常用设计模式
何为设计模式? 就是对一些常见问题进行归纳总结,并针对具体问题给出一套通用的解决办法(强调的是解决问题的思想): 在开发中,只要遇到这类问题,就可以直接使用这些设计模式解决问题. ---------- ...
- java中几种常用的设计模式
参考https://blog.csdn.net/jiyang_1/article/details/50110931 参考https://blog.csdn.net/dean_hu/article/de ...
- 聊聊Java中几种常用的设计模式
1.单例模式(有的书上说叫单态模式其实都一样) 该模式主要目的是使内存中保持1个对象.看下面的例子: package org.sp.singleton; //方法一 public class Sing ...
- java学习之三种常用设计模式
一.适配器设计模式 简单来说,就是通过一个间接类来选择性的来覆写一个接口 interface Window{ public void open() ; // 打开窗口 public void clos ...
- java中4种常用线程池
一.线程池 线程池:说白了,就是一种线程使用模式.线程过多会带来调度开销,进而影响整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,这避免了在处理短时间任务时创建与销毁线程的代价 ...
- JAVA中几种常用的RPC框架介绍
原文:https://blog.csdn.net/zhaowen25/article/details/45443951
随机推荐
- 前端学习(十七):JavaScript常用对象
进击のpython ***** 前端学习--JavaScript常用对象 JavaScript中的所有事物都是对象:字符串.数字.数组.日期,等等 在JavaScript中,对象是拥有属性和方法的数据 ...
- mac下高效安装 homebrew 及完美避坑姿势 (亲测有效)
世上无难事,只要找到 Homebrew 的正确安装方式. Homebrew 是什么 Homebrew是 mac的包管理器,仅需执行相应的命令,就能下载安装需要的软件包,可以省掉自己去下载.解压.拖拽( ...
- Python打开和关闭文件
Python打开和关闭文件: open(文件名,打开文件的模式[,寄存区的缓冲]): 文件名:字符串值 注:文件名带有后缀名 # 打开创建好的 test.txt 文件 f = open("t ...
- PHP strncmp() 函数
实例 比较两个字符串(区分大小写): <?php高佣联盟 www.cgewang.comecho strncmp("Hello world!","Hello ear ...
- ZROI 提高十连测 DAY2
总结:入题尽量快,想到做法要先证明是否正确是否有不合法的情况,是否和题目中描述的情景一模一样. 不要慌 反正慌也拿不了多少分,多分析题目的性质如果不把题目的性质分析出来的话,暴力也非常的难写,有 ...
- 剑指 Offer 50. 第一个只出现一次的字符
本题 题目链接 题目描述 我的题解 (方法三应用更广泛:方法一虽有限制,但很好用,此题中该方法效率也最高) 方法一:(适用于范围确定的) 思路分析 该字符串只包含小写字母,即字符种类最多26个 开一个 ...
- Hadoop学习之NCDC天气数据获取
期望目的 下载<Hadoop权威教程>里用到的NCDC天气数据,供后续在此数据基础上跑mapred程序. 操作过程 步骤一.编写简单的shell脚本,下载数据文件到本地文件系统 已知NCD ...
- windows:进程查杀
windows平台中,某些进程做了各种保护,比如hook了terminateProcess,又或者注册了进程终止函数的回调.当调用这些API或任务管理器终止该进程时,会被绕过,典型如某些杀毒软件,怎么 ...
- Springboot中的CommandLineRunner
CommandLineRunner接口的作用 在平常开发中可能需要实现在启动后执行的功能,Springboot提供了一种简单的实现方案,即实现CommandLineRunner接口,实现功能的代码在接 ...
- Ef Core增加Sql方法
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)] public class DbFunAttribute : Attri ...