1.概要
2.内容
当前:单实例 状态模式
模式价值:
1性能价值:提高效率,保证只创建一个对象。
2机能价值:对该对象从操作都作用在一个对象上,这是机能需要。
需求
| 动作 | 走状态 | 停状态 | 动作后状态 |
| 走行 | 不动作 | 走行 | 进入=>走行状态 |
| 停止 | 走行 | 不动作 | 进入=>停止状态 |
| 发射 | 辅助瞄准 | 精准瞄准 | 状态不变 |
测试用例
| 步骤 | 规格 |
| 初始化 | 停状态 |
| 停止 | 不动作 |
| 发射 | 精准瞄准 |
| 走行 | 走行 |
| 发射 | 辅助瞄准 |
| 走行 | 不动作 |
| 停止 | 停止 |
类图
1.标准类图
2.本实例类图

代码解析

代码
java实现
class Function{
public String mStr;
Function(String str){
mStr = str;
exe();
}
public void exe() {
System.out.println(mStr);
}
};
// 坦克
interface IStateTank{
//走
void run();
//停
void stop();
//射击
void shot();
}
// 上下文
interface IContext{
void changeState(IStateTank state);
void run();
void stop();
void shot();
}
///* abstract *//////////////////////////////////////
abstract class State implements IStateTank{
IContext mContext;
protected State(IContext c) {
mContext = c;
}
}
///* Concrete *//////////////////////////////////////
class RunStae extends State{
static IStateTank mStateTank = null;
private RunStae(IContext c) {
super(c);
}
public void run() {
new Function("机能不运行:走行==走行");
}
public void stop() {
mContext.changeState(StopStae.getRunState(mContext));
System.out.println("变更:走行=>停止");
}
public void shot() {
new Function("辅助瞄准");
}
static IStateTank getRunState(IContext c) {
if(mStateTank == null) {
mStateTank = new RunStae(c);
}
return mStateTank;
}
}
class StopStae extends State{
static IStateTank mStateTank = null;
private StopStae(IContext c) {
super(c);
}
public void run() {
new Function("走行机能运行");
mContext.changeState(RunStae.getRunState(mContext));
System.out.println("变更:停止=>走行");
}
public void stop() {
new Function("机能不运行:停止==停止");
}
public void shot() {
new Function("精准瞄准");
}
public static IStateTank getRunState(IContext c) {
if(mStateTank == null) {
mStateTank = new StopStae(c);
}
return mStateTank;
}
}
class ContextTank implements IContext{
IStateTank mState;
public ContextTank(){
mState = StopStae.getRunState(this);
}
public void changeState(IStateTank state) {
mState = state;
}
public void run() {
mState.run();
}
public void stop() {
mState.stop();
}
public void shot() {
mState.shot();
}
}
class A{
static A ma;
static public A get() {
return ma;
}
}
public class Client {
public static void main(String[] args) {
System.out.println("hello worldff !");
ContextTank tank = new ContextTank();
tank.stop();
tank.shot();
tank.run();
tank.shot();
tank.run();
tank.stop();
}
}
运行结果

说明
状态模式中state类Context类间的关系可以有多种处理方式,这里采用了单实例的方式来实现。
因为单实例没有什么太多可说的,即使+线程等各种情况,这也就是个需求而已。
没有太多的设计价值可体现,所以就在这里运用了一下作为举例了。
关于该模式如果涉及到线程还有很多控制的细节,不过那都是结合其他知识对该模式的运用,就模式而言,这篇应该够了。
如果对其他的感兴趣,如下是一篇不错的文章。
JAVA设计模式之单例模式(单件模式)—Singleton Pattern - CarryJack - 博客园
3.补充C#单件实现
类图

代码
using System;
namespace 单实例模式
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Program p = new Program();
p.main();
Console.ReadLine();
}
private void main() {
Singleton singleton_a = Singleton.Instance();
Singleton singleton_b = Singleton.Instance();
Console.WriteLine("设置 singleton_b 的SigeltonData为:5");
singleton_b.SingletonOperation(5);
Console.WriteLine("设置后 singleton_a 的SigeltonData为:" + singleton_a.GetSigeltonData());
}
}
class Singleton
{
private static Singleton uniqueInstance = new Singleton();
private int singletonData = 0;
public int GetSigeltonData()
{
return singletonData;
}
// 实例化
public static Singleton Instance()
{
return uniqueInstance;
}
// 单件的动作
public void SingletonOperation(int data)
{
singletonData = data;
}
}
}
运行结果

4.关联知识
1.单件模式
单例模式(Singleton Pattern)是软件设计中一种常用的创建型设计模式,其核心目标是确保一个类只有一个实例,并提供全局访问点。这种模式常用于需要控制资源(如数据库连接池、日志对象、配置管理器等)的场景,避免频繁创建和销毁对象带来的开销。
核心实现方式
1. 懒汉式(Lazy Initialization)
- 延迟加载:第一次使用时才创建实例。
- 线程不安全:需通过同步机制保证线程安全。
public class Singleton { private static Singleton instance; private Singleton() {} // 私有构造方法 public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
2. 饿汉式(Eager Initialization)
- 类加载时创建实例:线程安全,但可能浪费资源。
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }
3. 双重检查锁(Double-Checked Locking)
- 减少同步开销:仅在实例未初始化时加锁。
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
4. 静态内部类(Bill Pugh)
- 利用类加载机制:线程安全且无需同步。
public class Singleton { private Singleton() {} private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }
5. 枚举实现(Enum)
- 最简洁且防反射攻击:Effective Java推荐方式。
public enum Singleton { INSTANCE; public void doSomething() { // 方法实现 } }
关键注意事项
- 线程安全:多线程环境下需确保实例唯一性。
- 反射攻击:通过反射调用私有构造方法可能破坏单例(枚举方式天然免疫)。
- 序列化与反序列化:需实现
readResolve()方法防止反序列化创建新实例。 - 依赖注入:在框架(如Spring)中,单例通常由容器管理,避免手动实现。
适用场景
- 共享资源(如数据库连接池、线程池)。
- 全局配置管理。
- 日志记录器。
- 缓存系统。
潜在问题
- 全局状态:可能增加代码耦合度。
- 测试困难:单例状态可能影响单元测试。
- 扩展性受限:无法继承单例类(可通过组合模式优化)。
合理选择实现方式,根据具体场景权衡线程安全、性能与代码简洁性。
2.单件与享元
你提到的“单件与享元”应理解为单例模式(Singleton)与享元模式(Flyweight)的对比。以下是两者的核心区别和应用场景的详细分析:
1. 单例模式(Singleton)
- 核心目标:确保一个类只有一个实例,并提供全局访问点。
- 适用场景:
- 全局唯一资源(如数据库连接池、日志管理器)。
- 需要严格控制实例数量的场景(如配置中心)。
- 特点:
- 实例数量为1:通过私有构造方法和静态方法控制实例。
- 线程安全:需处理多线程环境下的同步问题。
- 潜在问题:全局状态可能增加耦合度,测试时需模拟单例。
示例:
public class Logger {
private static Logger instance;
private Logger() {} // 私有构造方法
public static synchronized Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String message) {
System.out.println("Log: " + message);
}
}
// 使用:Logger.getInstance().log("Error occurred");
2. 享元模式(Flyweight)
- 核心目标:通过共享对象减少内存占用,支持大量细粒度对象的高效管理。
- 适用场景:
- 需要大量相似对象(如游戏中的精灵、文档中的字符样式)。
- 对象创建成本高,且多数状态可共享。
- 特点:
- 区分内部状态(共享)与外部状态(不共享):内部状态存储在享元对象中,外部状态由客户端管理。
- 对象池管理:通过工厂按需分配或复用对象。
- 潜在问题:需合理划分内部/外部状态,否则可能增加复杂度。
示例:
// 享元接口
public interface Sprite {
void render(int x, int y); // 外部状态(位置)通过参数传入
}
// 具体享元类(共享纹理)
public class TextureSprite implements Sprite {
private String texture;
public TextureSprite(String texture) {
this.texture = texture;
}
@Override
public void render(int x, int y) {
System.out.println("Rendering texture " + texture + " at (" + x + ", " + y + ")");
}
}
// 享元工厂(管理对象池)
public class SpriteFactory {
private Map<String, Sprite> pool = new HashMap<>();
public Sprite getSprite(String texture) {
Sprite sprite = pool.get(texture);
if (sprite == null) {
sprite = new TextureSprite(texture);
pool.put(texture, sprite);
}
return sprite;
}
}
// 使用:
SpriteFactory factory = new SpriteFactory();
Sprite sprite1 = factory.getSprite("grass");
sprite1.render(10, 20);
Sprite sprite2 = factory.getSprite("grass"); // 复用相同纹理的实例
sprite2.render(30, 40);
关键区别
| 维度 | 单例模式 | 享元模式 |
|---|---|---|
| 目标 | 控制实例数量为1 | 减少对象数量,共享状态 |
| 对象数量 | 严格1个 | 多个,但共享内部状态 |
| 关注点 | 实例的唯一性 | 内存优化与状态管理 |
| 适用场景 | 全局唯一资源(如配置) | 大量重复对象(如游戏角色) |
| 副作用 | 引入全局状态,可能增加耦合 | 需划分内外部状态,管理复杂 |
总结
- 选择单例模式:当系统中需要全局唯一的资源(如配置、日志),且该资源本身不可共享时。
- 选择享元模式:当需要管理大量相似对象,且这些对象的大部分状态可以共享时(如游戏中的纹理、UI控件样式)。
通过合理区分两种模式的应用场景,可以避免因误用导致的资源浪费或设计复杂化。
3.可不可以把享元当做批量的单件
将享元模式(Flyweight)视为“批量的单例(Singleton)”是一种不准确的简化理解,虽然两者都涉及对象共享,但核心目标和实现方式存在本质区别。以下是关键分析:
1. 核心目标不同
- 单例模式:确保全局唯一实例,控制资源的唯一性(如数据库连接池、配置中心)。
- 享元模式:通过共享同类对象减少内存占用,支持大量细粒度对象的高效管理(如游戏中的精灵、文档样式)。
2. 对象数量与管理方式
- 单例模式:
- 严格1个实例,通过私有构造方法和静态方法控制。
- 无“批量”概念,仅维护单一实例。
- 享元模式:
- 允许多个实例,但按类型或内部状态共享。例如:
- 同一纹理的精灵共享一个实例,不同纹理的精灵可能有多个实例。
- 通过工厂管理对象池,按需复用或创建。
- 允许多个实例,但按类型或内部状态共享。例如:
3. 适用场景差异
- 单例模式:
- 全局资源(如日志、缓存)。
- 需要严格限制实例数量的场景。
- 享元模式:
- 对象数量庞大且状态重复(如游戏中的NPC、UI控件)。
- 对象创建成本高,需优化内存。
4. 示例对比
-
单例模式(全局日志):
public class Logger { private static Logger instance = new Logger(); private Logger() {} public static Logger getInstance() { return instance; } public void log(String message) { /* ... */ } } // 使用:Logger.getInstance().log("Error"); -
享元模式(游戏精灵):
// 享元接口 public interface Sprite { void render(int x, int y); // 外部状态(位置)由客户端传入 } // 具体享元类(按纹理共享) public class TextureSprite implements Sprite { private final String texture; public TextureSprite(String texture) { this.texture = texture; } @Override public void render(int x, int y) { /* 使用texture渲染 */ } } // 享元工厂(管理对象池) public class SpriteFactory { private Map<String, Sprite> pool = new HashMap<>(); public Sprite getSprite(String texture) { return pool.computeIfAbsent(texture, k -> new TextureSprite(k)); } } // 使用: SpriteFactory factory = new SpriteFactory(); Sprite grass = factory.getSprite("grass"); // 共享同一纹理 Sprite water = factory.getSprite("water"); // 不同纹理,创建新实例
5. 关键问题:为何不能简单视为“批量单例”?
- 单例的“唯一性”是全局的,而享元的“共享”是按类型或状态划分的。
- 若强行用单例实现“批量共享”,需为每个类型创建独立的单例类(如
GrassSpriteSingleton,WaterSpriteSingleton),导致:- 代码膨胀:类型增多时,单例类数量爆炸。
- 违反开闭原则:新增类型需修改工厂或添加新类。
- 失去享元的核心优势:动态管理对象池和状态分离。
总结
享元模式是“按类型共享对象”,而非简单的“批量单例”。单例强调全局唯一性,而享元通过细分内部状态(共享)和外部状态(不共享),在内存与优化对象管理上更具灵活性。若需管理大量同类对象,享元是更优雅的选择;若需全局唯一资源,单例更合适。
本文介绍了一个结合单实例模式的状态模式实现案例。通过坦克状态切换的例子,展示了如何使用状态模式来改变对象的行为,并利用单实例模式确保状态对象的唯一性。

943






