坦克大战状态模式+单实例实现实现

本文介绍了一个结合单实例模式的状态模式实现案例。通过坦克状态切换的例子,展示了如何使用状态模式来改变对象的行为,并利用单实例模式确保状态对象的唯一性。

1.概要

2.内容

目录:《一个实例讲完23种设计模式》

当前:单实例  状态模式

关联:状态模式-电控门-java

qt 用宏控制静态接口的统一 单件应用

 智能指针、单件模式、函数式编程

 创建单件函数_任何类都可单件

 泛型与单例模式在C#中的应用

c++ 单件模式解耦应用

 c++ 静态成员+单件模式 事件

模式价值:

1性能价值:提高效率,保证只创建一个对象。

2机能价值:对该对象从操作都作用在一个对象上,这是机能需要。

需求

动作 走状态 停状态动作后状态
走行不动作 走行进入=>走行状态
停止 走行   不动作 进入=>停止状态
发射 辅助瞄准精准瞄准  状态不变

测试用例

步骤规格
初始化停状态
停止不动作 
发射 精准瞄准
走行 走行   
发射 辅助瞄准
走行 不动作
停止停止 

类图

1.标准类图

       状态模式

       单实例模式

2.本实例类图

代码解析 

代码

js实现

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() {
            // 方法实现
        }
    }

关键注意事项

  1. 线程安全:多线程环境下需确保实例唯一性。
  2. 反射攻击:通过反射调用私有构造方法可能破坏单例(枚举方式天然免疫)。
  3. 序列化与反序列化:需实现readResolve()方法防止反序列化创建新实例。
  4. 依赖注入:在框架(如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. 关键问题:为何不能简单视为“批量单例”?

  • 单例的“唯一性”是全局的,而享元的“共享”是按类型或状态划分的
  • 若强行用单例实现“批量共享”,需为每个类型创建独立的单例类(如GrassSpriteSingletonWaterSpriteSingleton),导致:
    • 代码膨胀:类型增多时,单例类数量爆炸。
    • 违反开闭原则:新增类型需修改工厂或添加新类。
    • 失去享元的核心优势:动态管理对象池和状态分离。

总结

享元模式是“按类型共享对象”,而非简单的“批量单例”。单例强调全局唯一性,而享元通过细分内部状态(共享)和外部状态(不共享),在内存与优化对象管理上更具灵活性。若需管理大量同类对象,享元是更优雅的选择;若需全局唯一资源,单例更合适。

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值