三种工厂模式的区别和特点

典型回答 工厂模式的主要功能就是帮助我们实例化对象的。之所以名字中包含工厂模式四个字,是因为对象的实例化过程是通过工厂实现的,是用工厂代替new操作的。 这样做的好处是封装了对象的实例化细节,尤其是对于实例化较复杂或者对象的生命周期应该集中管理的情况。会给你系统带来更大的可扩展性和尽量少的修改量。 **工厂模式有三种,分别是简单工厂模式、工厂方法模式、抽象工厂模式。**三种模式从前到后越来越抽象,也更具有一般性。 **简单工厂 :一个工厂创建所有具体产品。**对于增加新的产品,主要是新增产品,就要修改工厂类。符合单一职责原则。不符合开放-封闭原则 优点: 1、屏蔽产品的具体实现,调用者只关心产品的接口。 2、实现简单 缺点: 1、增加产品,需要修改工厂类,不符合开放-封闭原则 2、工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则 **工厂方法 :一个工厂方法只创建一个具体产品。**支持增加任意产品,新增产品时不需要更改已有的工厂,需要增加该产品对应的工厂。符合单一职责原则、符合开放-封闭原则。但是引入了复杂性 优点: 1、继承了简单工厂模式的优点 2、符合开放-封闭原则 缺点: 1、增加产品,需要增加新的工厂类,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。 抽象工厂 :一个工厂方法只创建一类具体产品。增加新产品时,需要修改工厂,增加产品族时,需要增加工厂。符合单一职责原则,部分符合开放-封闭原则,降低了复杂性 优点: 1、隔离了具体类的生成,使得客户并不需要知道什么被创建 2、每次可以通过具体工厂类创建一个产品族中的多个对象,增加或者替换产品族比较方便,增加新的具体工厂和产品族很方便; 缺点 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性。 扩展知识 简单工厂模式 简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。 在介绍简单工厂模式之前,我们尝试解决以下问题: 现在我们要使用面向对象的形式定义计算器,为了实现各算法之间的解耦。主要的用到的类如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 // 计算类的基类 public abstract class Operation { private double value1 = 0; private double value2 = 0; public double getValue1() { return value1; } public void setValue1(double value1) { this.value1 = value1; } public double getValue2() { return value2; } public void setValue2(double value2) { this.value2 = value2; } protected abstract double getResule(); } //加法 public class OperationAdd extends Operation { @Override protected double getResule() { return getValue1() + getValue2(); } } //减法 public class OperationSub extends Operation { @Override protected double getResule() { return getValue1() - getValue2(); } } //乘法 public class OperationMul extends Operation { @Override protected double getResule() { return getValue1() * getValue2(); } } //除法 public class OperationDiv extends Operation { @Override protected double getResule() { if (getValue2() != 0) { return getValue1() / getValue2(); } throw new IllegalArgumentException("除数不能为零"); } } 当我想要执行加法运算时,可以使用如下代码: ...

March 22, 2026 · 4 min · santu

什么是不可变模式,有哪些应用?

在并发编程的世界里,共享变量的线程安全问题永远是一个无法避免且不得不面对的问题,如果只有读的情况,那么永远也不会出现线程安全的问题,因为多线程读永远是线程安全的,但是多线程读写一定会存在线程安全的问题。 那既然这么说是不是通过只读就能解决并发问题呢?其实最简单的办法就是让共享变量只有读操作,而没有写操作。这个办法如此重要,以至于被上升到了一种解决并发问题的设计模式:不变性(Immutability)模式。 所谓不变性,简单来讲,就是对象一旦被创建之后,状态就不再发生变化。换句话说,就是变量一旦被赋值,就不允许修改了(没有写操作);没有修改操作,也就是保持了不变性。 不可变性的类 在 java 中,如果要实现一个不可变的对象是很简单的,将其定义为 final 即可,同样类也是如此,只需要通过 final 来修饰某个类即可。同时将一个类所有的属性都设置成 final 的,并且只允许存在只读方法,那么这个类基本上就具备不可变性了。 更严格的做法是这个类本身也是 final 的,也就是不允许继承。因为子类可以覆盖父类的方法,有可能改变不可变性,所以推荐你在实际工作中,使用这种更严格的做法。 我们再日常开发中,已经在不知不觉中享受不可变模式带来的好处,例如经常用到的 String、Long、Integer、Double 等基础类型的包装类都具备不可变性,这些对象的线程安全性都是靠不可变性来保证的。 仔细翻看这些类的声明、属性和方法,你会发现它们都严格遵守不可变类的三点要求:类是 final 的,属性也是 final 的。同样的一旦某个类被 final 修饰,其本身就不能被继承了,也就无法重写其方法,即方法是只读的。 既然说方法是只读的,但是 Java 的 String 方法也有类似字符替换操作,这个不就已经改变了value[] 变量了吗?因为 value[] 是这么定义的。 我们结合 String 的源代码(jdk8)来看一下 jdk 是如何处理这个问题的,下面是源码的截图 它实际上是重新定义了一个新的 buf[] 来保存数据,这样在最后返回数据的时候确实确没有修改 原始的value[],而是将替换后的字符串作为返回值返回了。 通过分析 String 的实现,你可能已经发现了,如果具备不可变性的类,需要提供类似修改的功能,具体该怎么操作呢?做法很简单,那就是创建一个新的不可变对象,这是与可变对象的一个重要区别,可变对象往往是修改自己的属性。 所有的修改操作都创建一个新的不可变对象。但是一个问题的解决必然会带来的新的问题,那就是这样势必在每次使用的时候都会创建新的对象,那岂不是无端的降低了系统的性能了浪费了系统的资源?这个时候享元模式就可以大显神通了。 享元模式避免创建重复对象 享元模式你可能实际开发中使用的很少,它是这么定义的: 享元模式(Flyweight Pattern):是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。 通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元 看不懂没关系,用一句直白话来概括就是:通过对象池的技术来避免重复的创建对象。这就好比是 Spring 中的容器(单例模式下),我们的对象都交给 Spring 容器来管理,这样我们再使用的时候只需要到容器中去拿即可,而不是每次都去创建新的对象。 利用享元模式可以减少创建对象的数量,从而减少内存占用。Java 语言里面 Long、Integer、Short、Byte 等这些基本数据类型的包装类都用到了享元模式。 享元模式本质上其实就是一个对象池,利用享元模式创建对象的逻辑也很简单:创建之前,首先去对象池里看看是不是存在;如果已经存在,就利用对象池里的对象;如果不存在,就会新创建一个对象,并且把这个新创建出来的对象放进对象池里。 jdk 源码中是如何使用享元模式的呢?我们以 Long 这个类为例来解释说明下。 ...

March 22, 2026 · 2 min · santu

如何破坏单例模式?

典型回答 单例模式主要是通过把一个类的构造方法私有化,来避免重复创建多个对象的。那么,想要破坏单例,只要想办法能够执行到这个私有的构造方法就行了。 一般来说做法有使用反射及使用反序列化都可以破坏单例。 扩展知识 我们先通过双重校验锁的方式创建一个单例,后文会通过反射及反序列化的方式尝试破坏这个单例。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.hollis; import java.io.Serializable; /** * Created by hollis on 16/2/5. * 使用双重校验锁方式实现单例 */ public class Singleton implements Serializable{ private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } 反射破坏单例 我们尝试通过反射技术,来破坏单例: ...

March 22, 2026 · 4 min · santu

什么是设计模式?有什么好处?

典型回答 设计模式是在软件开发过程中经常遇到的问题的通用解决方案。它们是经过无数的验证和经验积累的最佳实践。 首先,设计模式是一些前人经验的一些总结,所以,当遇到相似的问题的时候,我们可以直接借鉴好的设计模式来实现,这样可以大大降低我们的试错成本和迭代成本。可以大大提升我们的开发速度。 不要认为只有23种,只要是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结都是软件设计模式的。比如说MVC 而且,设计模式都是遵守了很多设计原则的,这些原则可以帮助我们大大提升代码的可重用性、可维护性和可扩展性等。 ✅设计模式的7大基本原则有哪些? ✅使用哪种设计模式可以提高代码的复用性? ✅使用哪种设计模式可以提高代码可维护性? 设计模式通过使用松耦合的对象之间的交互,使系统更容易进行扩展和修改。通过引入抽象层和接口,可以将变化的部分隔离出来,而不影响整个系统的其他部分。提高了代码的可扩展性。 还有一点比较重要,那就是设计模式其实也是一种**通用语言。**他使得团队成员之间交流更加的顺畅。当我们提到单例、模板、策略、责任链,大家都知道是怎么回事儿。 可以很快速的就get到其他人要说的意思,不仅可以提升沟通效率,还能降低后续代码维护中的出错概率。

March 22, 2026 · 1 min · santu

使用哪种设计模式可以提高代码可维护性?

典型回答 有很多设计模式可以提高代码可维护性,以下列举几种常见的: MVC模式(Model-View-Controller):将应用程序分为三个部分,分别是模型(Model)、视图(View)和控制器(Controller),它们之间通过定义清晰的接口进行交互。这种模式可以将业务逻辑与用户界面分离,使得应用程序更容易维护和修改。 观察者模式(Observer):将对象之间的一对多的依赖关系封装起来,当一个对象发生改变时,它的所有依赖者都会得到通知并自动更新。这种模式可以减少对象之间的耦合度,使得系统更加灵活和易于维护。 装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,而不需要修改这个对象的代码。这种模式可以使得对象的功能扩展变得更加灵活和可控。 工厂模式(Factory):将对象的创建和使用分离,通过一个工厂类来负责创建对象。这种模式可以使得系统更加灵活和易于维护,也方便对对象的管理和测试。 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。这种模式可以保证对象的唯一性,并方便对象的管理和控制。

March 22, 2026 · 1 min · santu

使用哪种设计模式可以提高代码的复用性?

典型回答 工厂模式(Factory):通过一个工厂类来负责创建对象,将对象的创建和使用分离,可以避免在代码中重复写对象创建的代码。 模板方法模式(Template Method):通过模板方法,将一些通用的逻辑抽象出来放到父类中,各个子类继承父类并复用这些公共的方法。 装饰器模式(Decorator):动态地给一个对象添加一些额外的职责,可以避免在代码中重复编写相似的功能。 享元模式(Flyweight):将一个对象的状态分成内部状态和外部状态,多个具有相同内部状态的对象可以共享外部状态,可以避免在代码中重复创建具有相同内部状态的对象。 适配器模式(Adapter):将一个类的接口转换成客户端希望的另一个接口,可以使得原本不兼容的类可以协同工作。 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。这种模式可以保证对象的唯一性,并方便对象的管理和控制以及复用。

March 22, 2026 · 1 min · santu

设计模式的7大基本原则有哪些?

典型回答 设计模式是软件设计中常用的解决方案,它们有助于构建可重用、可维护和可扩展的代码。在设计模式中,有七个基本原则,它们提供了指导设计的准则和原则。 设计模式的7大基本原则中,有5个来源于面向对象的5大基本原则(SOLID原则),他们分别是: 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起变化的原因。换句话说,一个类应该只做一件事。这个原则鼓励将功能分解为小的、独立的单元,每个单元只负责一件事情。 开放封闭原则(Open/Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展是开放的,而对修改是关闭的。这意味着应该通过扩展现有代码来引入新功能,而不是直接修改已有的代码。 里氏替换原则(Liskov Substitution Principle,LSP):任何一个父类出现的地方,都可以用它的子类来替代,而不会导致程序的错误或异常。这个原则强调继承关系的正确使用,子类应该能够完全替代父类而不引发意外行为。 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。这要求我们写出来的程序要依赖于抽象接口,而不是具体的实现。这个原则通过引入抽象接口或抽象类,将高层模块与低层模块解耦。 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖于它不需要使用的接口。一个类不应该强迫它的客户端依赖于它们不需要的方法。接口应该小而专注,不应该包含多余的方法。这要求我们要使用多个小的专门的接口,而不要使用一个大的总接口。 除了这5大基本原则以外,在设计模式中,还有2个原则需要我们遵守的。那就是: 迪米特法则(Law of Demeter,LoD):一个对象应该对其他对象有最少的了解。对象之间应该保持松耦合。这个原则鼓励将复杂系统分解为许多小的、相对独立的模块,模块之间的交互通过最少的接口进行。 合成/聚合复用原则(Composition/Aggregation Reuse Principle,CARP):优先使用合成/聚合,而不是继承来实现代码复用。这个原则推崇对象组合和聚合的方式来构建复杂的对象结构,而不是依赖继承。

March 22, 2026 · 1 min · santu

你在工作中是如何使用设计模式的?

典型回答 工作中常用的设计模式有很多,如单例、工厂、策略、模板等。一般在工作中,是可以把策略、工厂和模板一起结合着来使用的。 当我们需要有多个具体的策略服务的时候,那不同的内容放到策略服务中,那些公共的东西就可以抽象出来放到模板方法中了。那这些策略服务该如何管理呢?什么时候用什么策略服务呢?这时候就可以借助工厂来管理这些服务。 如以下例子, 我们需要定义一个支付服务,里面有一个支付方法: 1 2 3 4 5 6 7 8 9 public interface PayService { public void pay(PayRequest payRequest); } class PayRequest { } 这是一个单独的接口,只定义了一个方法,那么,我们再把所有支付渠道中公共的代码抽出来,定义一个抽象类: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public abstract class AbstractPayService implements PayService { @Override public void pay(PayRequest payRequest) { //前置检查 validateRequest(payRequest); //支付核心逻辑 doPay(payRequest); //后置处理 postPay(payRequest); } public abstract void doPay(PayRequest payRequest); private void postPay(PayRequest payRequest) { //支付成功的后置处理 } public void validateRequest(PayRequest payRequest) { //参数检查 } } 这个抽象类中首先把pay方法给实现了,然后编排了几个其他的方法,这些公共的方法在抽象类中直接实现了,具体的支付核心实现,留给实现类去实现就行了。 ...

March 22, 2026 · 2 min · santu

单例模式的多种写法

典型回答 想要实现一个单例,首先就是要考虑把构造函数设置成private的,否则的话就可以随时通过构造函数创建对象了,就不是单例了。 那把构造函数private之后,就还需要提供一个方法,可以初始化单例对象,并且要保证只能初始化一个单例对象。并且需要考虑线程安全的问题。 具体到写法上,主要有5种。分别是懒汉、饿汉、静态内部类、双重校验锁以及枚举。 扩展知识 懒汉 所谓懒汉,就是在需要的时候才会去创建对象。 1 2 3 4 5 6 7 8 9 10 public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 好处就是避免提前创建浪费资源,但是缺点也明显,就是第一创建的时候浪费时间。 饿汉 所谓饿汉,就是在类刚一初始化的时候就立即把单例对象创建出来。 1 2 3 4 5 6 7 public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } } 1 2 3 4 5 6 7 8 9 10 public class Singleton { private Singleton instance = null; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return this.instance; } } 以上两种都是饿汉模式的具体实现。 ...

March 22, 2026 · 2 min · santu

请简述MVC模式的思想

典型回答 MVC模式(Model-View-Controller)是一种软件设计模式,它将应用程序分为三个部分:模型、视图和控制器。这个模式的目的是将应用程序的表示(视图)与处理(控制器)分开,以及将应用程序的数据和业务逻辑(模型)与表示和处理分开。 具体来说,MVC模式的思想如下: 模型(Model):表示应用程序的核心业务逻辑和数据。模型通常包含一些数据结构和逻辑规则,用于处理数据的输入、输出、更新和存储等操作。模型并不关心数据的显示或用户的交互方式,它只关注数据本身以及对数据的操作。 视图(View):表示应用程序的用户界面,用于显示模型中的数据。视图通常包含一些控件和元素,用于展示数据,并且可以与用户进行交互。视图并不关心数据的处理或存储方式,它只关注如何呈现数据以及如何与用户进行交互。 控制器(Controller):表示应用程序的处理逻辑,用于控制视图和模型之间的交互。控制器通常包含一些事件处理和动作触发等操作,用于响应用户的输入或视图的变化,并对模型进行操作。控制器通过将用户的输入转化为对模型的操作,从而实现了视图和模型之间的解耦。 MVC模式的核心思想是将应用程序的表示和处理分离开来,从而使得应用程序更加灵活、易于维护和扩展。这种模式可以提高代码的可读性和可维护性,同时也可以促进代码的复用和分工,使得多人协作开发变得更加容易。

March 22, 2026 · 1 min · santu

留言给博主