Java程序员不得不知道的一些设计模式

news2025/2/11 7:44:25

1、什么是设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。 一些简单的设计模式略过,如常量接口模式、标识类型模式。 以下,将从最简单的设计模式开始讲起:

2、单例模式 Singleton

单例模式是指一个类只能拥有唯一的实例。为了保证这一点,我们必须要限制类的创建。即某个单例类,只可以实例化一次。那怎么才可以做到呢? 实现思路如下:

给需要单例的类声明私有的构造方法。这样就限制了其他类对此类的随意创建。 在单例类内部,声明自己的静态成员变量,且实例化自己。由在当前类中,是可以访问自己的所有私有成员及方法的,所以,即使构造方法为私有也可以成功创建对象。 一般情况下,再提供一个静态方法以返回创建的喻一对象。一般情况下,我们将此方法取名为newInstace,getInstance等,不过这并不是硬性的规定。 示例代码如下:

public class SingletonDemo {
 //私有化构造方法
 private SingletonDemo(){}
 //声明静态私有成员变量
 private static SingletonDemo demo;
 //提供静态工厂方法
 public static SingletonDemo getInstance(){
  if(demo==null){
   demo = new SingletonDemo();
  }
  return demo;
 }
}

4、多例模式 Multition

既然有单例,就会有多例。多例是指限定一个类的只能创建N(具体数量)个的设计模式。如以下性别类,就只能创建两个实例。实现思路是在本类中声明多个类的实例,并提供静态方法返回不同的实例即可:

第一种实现:

public class Gender {
 private String sex;
 private Gender(String sex){
  this.sex = sex;
 }
 //男性
 private static Gender MALE;
 //女性
 private static Gender FEMALE;
 public static Gender getMale(){
  if(MALE==null){
   MALE = new Gender("男");
  }
  return MALE;
 }
 public static Gender getFemale(){
  if(FEMALE==null){
   FEMALE = new Gender("女");
  }
  return FEMALE;
 }
}

第二种实现:

public class Gender {
 private String sex;
 private Gender(String sex){
  this.sex = sex;
 }
 //男性
 private static Gender MALE;
 //女性
 private static Gender FEMALE;
 static{//静态代码块中初始化
  MALE = new Gender("男");
  FEMALE = new Gender("女");
 }
 public static Gender getMale(){
  return MALE;
 }
 public static Gender getFemale(){
  return FEMALE;
 }
}

第三种实现:直接使用枚举

public enum Gender{
 MALE("男"),FEMALE("女");
 private String sex;
 private Gender(String sex){
  this.sex=sex;
 }
 @Override
 public String toString() {
  return sex;
 }
}

5、工厂方法模式Factory Method

工厂方法指在一个类内部,提供一个静态方法,返回自己的实例或是其他类的实例。具体是返回自己的实例,还是返回其他类的实例,这个要看具体业务的需求。我们的数据连接池返回的就是其他对象的实例:

public class FactoryDemo{
 private static Connection con;
 static{
 //在此连接数据库
 con = DriverManager.getConnection(…);
}
//提供一个静态工厂方法
public static Connection getConn(){
 return con;
}
}

6、适配置器模式 Adapter

一个接口,往往定义很多方法,但实现此接口的类,可能只需要几个方法。那么那些多余的方法,占据了我们大量的代码,将失去意义。那怎么才可以即省去一些无用的方法,又实现此接口呢?这就是适配置器模式。 Awt开发中的Listener大量使用了此模式。如WindowListener和WindowAdapter。 请见上面两个类(WindowListener和WindowAdapter)的源代码。

7、装饰器设计模式(Decorator)也叫包装设计模式(Wrapper)

如果一个类,即是某个类的子类,又拥有某个类的一个成员变量,则被叫做包装模式。 如:

public Interface IA{}  //定义一个接口
public class B implements IA{  //首先B是IA的子类
 private IA ia;     //然后B拥有一个IA的成员变量。
}

使用装饰器或是包装模式一般是为了增强类的功能。如增强某个方法等。

8、代理模式 Proxy

代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。 假设有一个Italk接口,有空的方法talk()(说话),所有的people对象都实现(implements)这个接口,实现talk()方法,前端有很多地方都将people实例化,执行talk方法,后来发现这些前端里有一些除了要说话以外还要唱歌(sing),那么我们既不能在Italk接口里增加sing()方法,又不能在每个前端都增加sing方法,我们只有增加一个代理类talkProxy,这个代理类里实现talk和sing方法,然后在需要sing方法的客户端调用代理类即可,代码如下:

//定义接口
interface ITalk {
 public void talk(String msg);
}
//定义一个代理,接收ITalk,并增加一个方法sing
class TalkProxy implements ITalk{
 private ITalk talker;
 private String music;
 public TalkProxy(ITalk talker,String music){
  this.talker=talker;
  this.music = music;
 }
 
 @Override
 public void talk(String msg) {
  talker.talk(msg);
  sing();
 }
 //再多定义一个唱歌方法
 private void sing(){
  System.err.println("正在唱:"+music);
 }
}
//接口的一个实现类
class Tom implements ITalk{
 public void talk(String msg) {
  System.err.println("正在说话:"+msg);
 }
}
//测试调用
public class ProxyDemo{
 public static void main(String[] args) {
  TalkProxy proxy = new TalkProxy(new Tom(),"十里香");
  proxy.talk("Hello");
 }
}

9、动态代理 Dync Proxy

在JDK中,Proxy类和InvocationHandler可以在不添加任何代码的情况下增强某个方法:

package cn.arcstack.pattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
//测试代码
public class DyncProxyDemo {
 public static void main(String[] args) {
  List list =(List) MyProxy.getProxy(new ArrayList());
  list.add("Hello");
 }
}
//书写代理类
class MyProxy implements InvocationHandler{
 //声明被代理之前的对象
 private Object src;
 //私有构造方法
 private MyProxy(Object src){
  this.src=src;
 }
 //提供一个静态方法以返回被代理以后的对象
 public static Object getProxy(Object src){
  Object dest = null;//被代理以后的对象
  try{
   //处理代理的核心代码
   dest = Proxy.newProxyInstance(MyProxy.class.getClassLoader(),
      src.getClass().getInterfaces(),
      new MyProxy(src));
  }catch(Exception e){
   throw new RuntimeException(e.getMessage(),e);
  }
  return dest;
 }
 //拦截方法
 public Object invoke(Object proxy, Method method, Object[] args)
   throws Throwable {
  System.err.println("正直执行方法:"+method.getName());
  Object oo = method.invoke(src, args);
  return oo;
 }
 
}

10、观察者模式Observer

观察者模式又叫做发布订阅模式。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。如Awt中的监听器Listener都是采用了种模式。 在此种模式下,观察者只是一个接口,即定义一个规范,具体的实现应该是客户端完成。就像Swing中的注册一个监听器一样如:

JFrame frame = new Jframe();//声明被观察,被监听者对象
frame.addWindowListener(new WindowListener(){…});//添加一个实现WindowListener的观察者
frame.setVisible(true); //执行一些后续的操作,只要注册了观察者,就会被观察者所监听到

示例代码如下:

public class ObserverDemo {
 public static void main(String[] args) {
  //杰克逊对象
  Singer jackson = new Singer();
  //注册观察者
  jackson.addListener(new SingListener() {
   public void listener(Singer singer) {
    System.err.println("监听到了手的正在唱歌。。。。");
   }
  });
  //唱歌
  jackson.sing();
 }
}
//设置一个监听器接口-监听谁在唱歌
interface SingListener{
 public void listener(Singer singer);
}
//一个可能的被监听者
class Singer{
 //声明观察者
 private SingListener listener;
 public void addListener(SingListener sl){
  this.listener=sl;
 }
 public void sing(){
  //判断是否有观察者
  if(this.listener!=null){
   this.listener.listener(this);
  }
  System.err.println("歌手正在唱歌");
 }
}

给观察者添加一个观察事件对象,SingEvent,以便于解藕: 完整的示例代码如下:

package cn.arcstack.pattern;
//测试代码
public class ObserverDemo {
 public static void main(String[] args) {
  Singer jackson = new Singer();
  jackson.addListener(new SingListener() {
   public void listener(SingEvent event) {
    System.err.println("歌手是:"+event.getSource());
   }
  });
  jackson.sing();
 }
}
//设置一个监听器接口-监听谁在唱歌
interface SingListener{
 public void listener(SingEvent event);
}
//设计一个事件对象
class SingEvent{
 //接收事件对象,即被观察者
 private Object source;
 public SingEvent(Object source){
  this.source=source;
 }
 public Object getSource() {
  return source;
 }
}
//一个可能的被监听者
class Singer{
 //声明观察者
 private SingListener listener;
 public void addListener(SingListener sl){
  this.listener=sl;
 }
 public void sing(){
  //判断是否有观察者
  if(this.listener!=null){
   this.listener.listener(new SingEvent(this));
  }
  System.err.println("歌手正在唱歌"+this);
 }
}

11、策略模式Strategy

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 策略模式的组成 -抽象策略角色: 策略类,通常由一个接口或者抽象类实现。   -具体策略角色:包装了相关的算法和行为。 -环境角色:持有一个策略类的引用,最终给客户端调用。

package cn.arcstack.pattern;
/**
 * 策略模式
 */
public class StrategyDemo {
 public static void main(String[] args) {
  Context ctx = new Context(new JavaProgrammer());
  ctx.doSth();
 }
}
//设计一个公共的接口
interface IStrategy{
 public void doSth();
}
//分别书写多个实现类,有不同的实现策略
class JavaProgrammer implements IStrategy{
 public void doSth() {
  System.err.println("写Java代码");
 }
}
class PhpProgrammer implements IStrategy{
 public void doSth() {
  System.err.println("写PHP代码");
 }
}
//实现策略类
class Context{
 private IStrategy strategy;
 public Context(IStrategy strategy){
  this.strategy = strategy;
 }
 //具体调用
 public void doSth(){
  this.strategy.doSth();
 }

12、门面设计模式

门面设计模式的结构:

alt

没有门面设计模式的调用方法:

alt

有了门面设计模式的类框架图如下:即由原来的客户端一一调用每一个系统,修改成一使用一个统一中调用:

alt

具体实现代码略。

13、命令模式Command

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern)

alt

示例代码如下:

package cn.arcstack.pattern;
public class CommandDemo {
 public static void main(String[] args) {
  //命令的接收者只有一个
  Receiver receiver = new Receiver();
  //定义命令的执行者
  Invoker invoker = new Invoker();
  //通过Command类具体通知命令的接收者做什么工作
  invoker.setCommand(new CommandA(receiver));
  invoker.execute();
  //再设置第二个命令
  invoker.setCommand(new CommandB(receiver));
  invoker.execute();
 }
}
//定义命令
abstract class Command{
 //定义接收命令对象
 protected Receiver receiver;
 public Command(Receiver rec){
  this.receiver=rec;
 }
 public abstract void execute();
}
//指定接收者的工作
class Receiver{
 public void actionA(){
  System.err.println("做第一件事");
 }
 public void actionB(){
  System.err.println("做第二件事");
 }
}
//执行者
class Invoker{
 //接收一个命令
 private Command command;
 public void setCommand(Command cmd){
  this.command = cmd;
 }
 //执行命令
 public void execute(){
  command.execute();
 }
}
//定义两个具体的命令
class CommandA extends Command{
 public CommandA(Receiver rec) {
  super(rec);
 }
 @Override
 public void execute() {
  this.receiver.actionA();
 }
}
//定义两个具体的命令
class CommandB extends Command{
 public CommandB(Receiver rec) {
  super(rec);
 }
 @Override
 public void execute() {
  this.receiver.actionB();
 }
}

14、原型模式Prototype

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。 它主要面对的问题是:“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。   如何使用?   因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单。 代码略。

15、亨元模式flyweight

可以理解成为轻量级模式,是一种软件设计模式。 面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题。但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价。那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作? 见下面的代码: //使用Integer对象的示例 Integer b1 = 100; Integer b2 = 100; System.err.println(b1==b2); //true //由于大量细粒度的对象出现在128以下,所以,下现的值为false Integer b3 = 128; Integer b4 = 128; System.err.println(b3==b4); //false

16、总结

在实际的开发中,单例、多例、工厂、适配置器、代理、装饰、包装是经常使用的发模式,其他模式只需要了解就可以了。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/640052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【arXiv2303】Learning with Explicit Shape Priors for Medical Image Segmentation

Learning with Explicit Shape Priors for Medical Image Segmentation, aXiv2303 解读:SPM: 一种即插即用的形状先验模块,可轻松嵌入任意编解码架构,助力涨点并显著改善分割效果! (qq.com) 论文:https://arxiv.org/…

基于Air103的DAP-link的硬件介绍

原文及固件链接 视频介绍链接 xkb7070-z 自锁式按键 上电开关 WAFER-SH1.0-6PWB 1.0间距卧贴插座 下载及通信接口 A2-4PA-2.54DS 4Pin接插件 纯下载接口 Air32F103CBT6 lqfp48 216MHz 256K Flash 32K RAM UD/UD- DP&DM信号 USB的差信号 R1 DIO监听 限流 PM254-2-04-W…

【3DsMAX】从零开始建房(7)

目录 1. 制作屋顶小船剩余部分 2. 制作小广告牌 1. 制作屋顶小船剩余部分 新建一个平面 长度分段设置为1 转换成可编辑多边形后,对边进行缩放 同样的方法再添加一个平面 添加“壳” 新建一个圆柱体作为桅杆 选中圆柱的底面,点击插入 挤出 将顶部的点缩…

深入理解深度学习——Transformer:编码器(Encoder)部分

分类目录:《深入理解深度学习》总目录 Transformer中的编码器不止一个,而是由一组 N N N个编码器串联而成。一个编码器的输出作为下一个编码器的输入。在下图中有 N N N个编码器,每一个编码器都从下方接收数据,再输出给上方。以此…

7年测试经验之谈,什么是模糊测试?

背景:近年来,随着信息技术的发展,各种新型自动化测试技术如雨后春笋般出现。其中,模糊测试(fuzz testing)技术开始受到行业关注,它尤其适用于发现未知的、隐蔽性较强的底层缺陷。这里&#xff0…

类的多继承的派生类的虚表的一些问题

虚表保存的其实并不是虚函数的地址,而是他的到jmp地址。 上我们的操作代码 class A { public:virtual void func1(){}virtual void func2(){}int a 1; };class B { public:virtual void func1(){}virtual void func2(){}int b 2; };class C : public A, public …

SAP HANA内存

用着用着HANA 数据库就慢了,原因都出在内存。 内存不足无非几个原因: 1.你的机器物理内存不足,这个好办,花钱扩。 2.你的HANA License容量不足,这个也好办,申请更大容量的内存License 3.你机器分配给HAN…

爬虫案例-使用Session登录指定网站(JS逆向AES-CBC加密+MD5加密)

总体概览:使用Session登录该网站,其中包括对password参数进行js逆向破解 (涉及加密:md5加密AES-CBC加密) 难度:两颗星 目标网址:aHR0cHM6Ly93d3cuZnhiYW9nYW8uY29tLw 下面文章将分为四个部分…

在后大流行时代利用Airbnb实现逆周期增长

回望近十年共享经济的发展历程,谁也不曾想到,最被看好的共享经济代表Uber竟在连年亏损后忍痛IPO,上市首日即破发,而主打「互联网房地产」模式的独角兽WeWork则上市失败,迅速失血,一度走到破产边缘。作为“共…

模型剪枝:Network Slimming剪枝实战

本文来自公众号“AI大道理” ​ Network Slimming剪枝是比较广泛的一种模型剪枝方法,作者来自清华大学、英特尔中国实验室、复旦大学和科内尔大学。 1、Network Slimming剪枝理论 Network Slimming剪枝是结构性剪枝,是通道剪枝,是静态剪枝…

mac安装hive_20230609

竟然是今年第一篇 hhhh 过两天把上半年的东西梳理好的话 陆续放上来吧~ 公司本地测试环境的hive版本不支持不等式关联操作,而现在用hive也比较多,所以在本地装了一个hive,主要写一下大致步骤和过程中遇到的问题~&#…

win10任务栏卡死解决

现象: win10 更新后,开机任务栏卡死,点开始反应,设置页面无法打开。 原因: 原因是Win10更新的任务栏资讯和兴趣,而资讯和兴趣是Edge浏览器的,该服务器是在国外,国内的网络加载不出来…

【lvs集群】HAProxy搭建Web集群

HAProxy搭建Web集群 一、 HAProxy简介1.1HAProxy主要特性1.2HAProxy负载均衡策略非常多,常见的有如下8种1.3LVS、Nginx、HAproxy的区别1.4常见的Web集群调度器 二、Haproxy搭建 Web 群集haproxy服务器部署节点服务器部署 三、定义监控页面与定义日志3.1定义监控页面…

stm32f103最小系统板详细介绍

一.什么是单片机最小系统 常见的单片机最小系统为单片机能独立运行程序及控制外围电路的最简单电路,主要由单片机、晶振电路、复位电路三部分构成。Stm32f103c8t6也不例外,构成最小的运行电路也需要以上三部分。 Stm32f103最小系统板原理图如下&#xf…

初始Sentinel(Sentinel的简单介绍及项目整合)

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章将详细介绍Sentinel的概念,优点,与Hystrix的对比以及微服务中整合Sentinel,后续文章将详细介绍Sentinel的细节部分。 如果文章…

分享几个关于AI的网站

分享几个关于AI的网站 AI文本 ChatGPT:https://chat.openai.com/ NotionAI:https://www.notion.so/product/ai A.I. Data Sidekick:AI工具编写 SQL、文档等的速度提高10倍https://www.airops.com/ Writesonic:人工智能写作辅助工…

CSS基础学习--7 fonts字体

一、CSS 字体 CSS字体属性定义字体系列,加粗,大小,文字样式。 二、字体系列 font-family 属性设置文本的字体系列 font-family 属性应该设置几个字体名称作为一种"后备"机制,如果浏览器不支持第一种字体,…

Haproxy的应用

Taproxy 一、Haproxy的原理Haproxy的主要特性Haproxy八种负载均衡策略LVS、Nginx、Haproxy的区别 二、搭建web群集 一、Haproxy的原理 Haproxy是可提供高可用性,负载均衡以及基于TCP和HTTP应用的代理,是免费、快速并且可靠的一种解决方案。Haproxy非常适…

电脑提示vcruntime140_1.dll丢失怎么修复?

本修复教程操作系统:Windows系统 vcruntime140_1.dll是电脑文件中的dll文件(动态链接库文件)。如果计算机中丢失了某个dll文件,可能会导致某些软件和游戏等程序无法正常启动运行,并且导致电脑系统弹窗报错。 在我们打…

FPGA基础知识-模块和端口

目录 学习目标 学习内容 端口 端口列表 端口声明 端口链接规则 学习时间 总结 学习目标: 1.说明Verilog 模块定义中的各个组成部分,例如模块名、端口列表、参数、变址声明、数据流描述语句、行为语句、调用(实例引用》其他模块以及任务…