模式
简介
软件设计模式,又称设计模式
- 是一套被反复使用、多数人知晓的,经过分类编目的,代码设计的经验总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案
- 使用设计模式是为了重用代码,让代码更容易被他人理解,保证代码的可靠性
- 合理运用设计模式可以完美的解决很多问题,每种模式都描述了在一个在我们中尉不断发生的问题,以及问题的核心解决方案
单例模式
1. 定义
??一个类只有一个实例,且该类能够自行创建这个实例的模式。 ??确定某个类与且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象应该有且只有只一个 exp: Windows只能打开一个任务管理器
2. 结构
- 私有的静态的实例对象 private static instance
- 私有的构造函数 (保证在该类外部,无法通过new的方式来创建对象实例) private Singleton(){}
- 公有的、静态的、访问该实例对象的方法 public static Singleton getInstance(){}
3. 常见实现方式
1. 懒汉模式
public class Sington {
private static Sington instance = null;
private Sington() {
System.out.println("Only once!");
}
private Sington(){}
public static Sington getInstance(){
if(instance == null){
instance = new Sington();
}
return instance;
}
}
这段代码中未考虑线程安全,可能还会有参数多个实例,要对线程安全问题进行解决
public class Sington {
private static Sington instance = null;
private Sington(){}
public static synchronized Sington getInstance(){
if(instance == null){
instance = new Sington();
}
return instance;
}
}
通过关键字synchronized 确保线程getInstance 安全。 弊端较大:
- 所有线程达到该方法后需要进行排队等候,对性能的损耗非常大
2. 饿汉模式
public class Sington {
private Sington(){
System.out.println("创建NoLazySingleton实例一次!");
}
private static Sington instance = new Sington();
public static Sington getInstance(){
return instance;
}
}
饿汉模式 | 懒汉模式 |
---|
加载时已存在一个实例 | 加载前无实例,加载后实例化,不一定需要使用 | 保证线程安全,采用JVM类加载的特性 | 采用synchronized保障线程安全 |
JVM在加载类的时候采用单线程
3. 双重校检锁
public class Sington {
private static Sington instance = null;
private Sington(){}
public static Sington getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
说明:
- 双重检索也是一种延迟加载
- 较好的解决了确保线程安全的时候效率低下的问题
单例模式的实现原理:
- 一重校检: 也就是第一个if(singleton==null),这个是为了代码提高代码执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance方法就不必要进入同步代码块,不用竞争锁。直接返回前面创建的实例
- 也就是第二个if(sington==null),这个校验是防止二次创建实例,假如有一种情况,当singleton还未被创建时,线程t1调用getInstance方法,由于第一次判断,此时线程t1准备继续执行,但是由于资源被线程t2抢占了,此时t2页调用getInstance方法,同样的,由于singleton并没有实例化,t2同样可以通过第一个if,然后继续往下执行,同步代码块,第二个if也通过,然后t2线程创建了一个实例singleton。此时t2线程完成任务,资源又回到t1线程,t1此时也进入同步代码块,如果没有这个第二个if,那么,t1就也会创建一个singleton实例,那么,就会出现创建多个实例的情况,但是加上第二个if,就可以完全避免这个多线程导致多次创建实例的问题
4. 静态内部类
public class Sington {
private static class SingtonHolder{
private static Sington instance = new Sington();
}
private Sington(){}
public static Sington getInstance(){
return SingtonHolder.instance;
}
}
特性:
- 加载时不会加载内部静态类,使用时才会进行加载
- 使用线程加载又是安全的
5. 枚举
public enum Singleton {
INSTANCE;
}
3. 特点
- 单例模式只有一个实例对象
- 该单例对象必须由单例类创建
- 单例类对外提供一个访问该单例的全局访问点
4. 优缺点
优点:
- 内存只存在一个实例,减少内存开销
- 避免对资源的多重占用
- 设置全局访问点,优化和共享资源访问
缺点:
- 一般没有接口,扩展困难
- 不利于代码调试
工厂模式
1. 定义
- 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
- 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2. 常见分类模式
1. 简单工厂模式
“产品”: 被创建的对象 “工厂”: 创建产品的对象 如果创建的产品不多,只要一个工厂类就可以完成,称为“简单工厂模式”,也叫做“静态工厂方法模式” 组成:
- 简单工厂:核心,负责创建所有实例的内部逻辑
- 抽象产品:创建所有对象的父类,负责描述所有实例共有的公共接口
- 具体产品:创建目标
public class Client {
public interface Product {
void show();
}
static class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具体产品1显示...");
}
}
static class ConcreteProduct2 implements Product {
public void show() {
System.out.println("具体产品2显示...");
}
}
final class Const {
static final int PRODUCT_A = 0;
static final int PRODUCT_B = 1;
static final int PRODUCT_C = 2;
}
static class SimpleFactory {
public static Product makeProduct(int kind) {
switch (kind) {
case Const.PRODUCT_A:
return new ConcreteProduct1();
case Const.PRODUCT_B:
return new ConcreteProduct2();
}
return null;
}
}
}
优点:
- 包含必要的逻辑判断,可以决定在什么时间创建哪一个产品的实例
- 客户只需要知道参数
- 可引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类
缺点:
- 工厂类单一,一旦异常,整个系统受影响
- 代码臃肿,违背高聚合原则
- 增加系统中类的个数,增加系统的复杂度和理解难度
- 扩展困难,增加新产品需要修改工厂逻辑
- 工厂角色不能形成基于继承的等级结构
2. 工厂方法模式
“工厂方法模式”是对简单工厂模式进一步抽象,可以在不修改原系统代码的情况下引进新产品,满足开闭原则 组成:
- 抽象工厂:提供创建产品的接口,通过抽象工厂访问具体工厂创建产品
- 具体工厂:实现抽象工厂中的抽象方法,完成创建
- 抽象产品:产品规范
- 具体产品:实现抽象产品角色定义的接口,与具体工厂一一对应
public class AnimalFarmTest {
public static void main(String[] args) {
try {
Animal a;
AnimalFarm af;
af = (AnimalFarm) ReadXML2.getObject();
a = af.newAnimal();
a.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
interface Animal {
public void show();
}
class Horse implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("工厂方法模式测试");
public Horse() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void show() {
jf.setVisible(true);
}
}
class Cattle implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("工厂方法模式测试");
public Cattle() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:牛"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void show() {
jf.setVisible(true);
}
}
interface AnimalFarm {
public Animal newAnimal();
}
class HorseFarm implements AnimalFarm {
public Animal newAnimal() {
System.out.println("新马出生!");
return new Horse();
}
}
class CattleFarm implements AnimalFarm {
public Animal newAnimal() {
System.out.println("新牛出生!");
return new Cattle();
}
}
package FactoryMethod;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
class ReadXML2 {
public static Object getObject() {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src/FactoryMethod/config2.xml"));
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = "FactoryMethod." + classNode.getNodeValue();
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
优点:
- 用户通过具体工厂名称就可以得到所要的产品,无需知道创建过程
- 灵活性强,
- 解耦框架,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
- 类过多,增加复杂度
3. 抽象工厂模式
1. 定义
??是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。 抽象工厂可以生产多个等级的产品
模式:
- 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
- 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
组成:
- 抽象工厂
- 具体工厂
- 抽象产品
- 具体产品
public class FarmTest {
public static void main(String[] args) {
try {
Farm f;
Animal a;
Plant p;
f = (Farm) ReadXML.getObject();
a = f.newAnimal();
p = f.newPlant();
a.show();
p.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
interface Plant {
public void show();
}
class Fruitage implements Plant {
public Fruitage() {
System.out.println("具体水果类生成");
}
public void show() {
System.out.println("执行水果类的相应操作");
}
}
class Vegetables implements Plant {
public Vegetables() {
System.out.println("具体蔬菜类生成");
}
public void show() {
System.out.println("执行蔬菜类的相应操作");
}
}
interface Animal {
public void show();
}
class Horse implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("抽象工厂模式测试");
public Horse() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void show() {
jf.setVisible(true);
}
}
interface Farm {
public Animal newAnimal();
public Plant newPlant();
}
class SGfarm implements Farm {
public Animal newAnimal() {
System.out.println("新牛出生!");
return new Cattle();
}
public Plant newPlant() {
System.out.println("蔬菜长成!");
return new Vegetables();
}
}
优点:
- 类内部对产品组相关联多等级产品共同管理
- 需要产品族,抽象工厂使用同一产品组
缺点 - 增强拓展性
总结
- 使用设计模式的目的是为了提高代码的可重用性、代码的可读性和代码的可靠性
- 懒汉模式将实例化时机存放到需要使用的时候,“延迟加载”,避免在加载中实例化用不到的实例,但是需要解决线程安全问题
- 双重校检锁是一种延迟加载,并且可以较好的解决在确保线程安全的前提下效率低下的问题,锁住部分代码。
- 对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数
- 抽象工厂模式当增加一个产品族满足开闭原则,添加一个新种类的产品,不满开闭原则
参考
设计模式——单例模式 JAVA设计模式系列——工厂模式 设计模式系列——抽象工厂模式 深入理解单例模式(附简单案例) 抽象工厂模式(详解版)
|