今天写类胡闹厨房的游戏适当的用接口真的很舒服,在像胡闹厨房这样的游戏开发里面把Counters谨慎的继承在一个BaseCounter里面然后在BaseCounter写所有物品接口函数真的很清爽代码。。
using UnityEngine;
public class BaseCounter : MonoBehaviour, IKitchenObjectParent
{
[SerializeField] private Transform counterTopPoint; // 放置物品的位置
private KitchenObject kitchenObject; // 物品的实例
//E键,方便让继承他的类复写用virtual
public virtual void Interact(Player player)
{
Debug.Log("BaseCounter.Interact();");
}
//F键,方便让继承他的类复写用virtual
public virtual void InteractAlternate(Player player)
{
Debug.Log("BaseCounter.InteractAlternate();");
}
#region 物品接口类函数
// 获取物品的跟随点位置(用于设置物品位置)
public Transform GetKitchenObjectFollowTransform()
{
return counterTopPoint;
}
public void SetKitchenObject(KitchenObject kitchenObject)
{
this.kitchenObject = kitchenObject; // 记录物品的实例
}
// 获取当前物品
public KitchenObject GetKitchenObject()
{
return kitchenObject;
}
// 清空当前物品
public void ClearKitchenObject()
{
kitchenObject = null;
}
// 是否有物品
public bool HasKitchenObject()
{
return kitchenObject is not null;
}
#endregion
}
1. 定义行为的契约
接口是一个契约,它规定了实现该接口的类需要提供哪些方法和事件。这样,接口的使用者无需关心具体的实现细节,只需要依赖接口定义的方法和事件。这提供了高度的抽象性。
举个例子:
假设你有一个 IProgress
接口,定义了一个 OnProgressChanged
事件。这意味着任何实现 IProgress
接口的类都可以使用这个事件。
public interface IProgress
{
event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
}
任何实现这个接口的类(比如 CookingProcess
或 LoadingProcess
)都应该提供进度变化的事件通知,而调用者只关心接口定义,甚至不关心具体是哪个类在使用这个接口。
2. 统一的事件处理机制
接口使得不同类型的对象可以通过统一的方式进行事件处理。你可以用相同的方式订阅和处理不同类实例的事件,即使它们在逻辑上有很大差异。
举个例子:
有多个类实现了 IProgress
接口,比如 CookingProcess
和 LoadingProcess
。它们都触发 OnProgressChanged
事件,UI 或其他代码可以统一使用 IProgress
来订阅和处理这些事件,而无需关心具体是哪个类。
public void SubscribeToProgress(IProgress progress)
{
progress.OnProgressChanged += (sender, args) =>
{
// 处理进度更新,更新进度条
};
}
(注,我用了lambda表达式,这样我认为代码少一点看起来清爽一点)
这样,不管传递的是 CookingProcess
还是 LoadingProcess
,你都可以使用相同的事件处理方法。
3. 解耦合
接口帮助解耦业务逻辑和事件的发布者/订阅者。通过接口,类与类之间的依赖变得更加松散。例如,UI类可以订阅 IProgress
接口的事件,而无需知道到底是哪个具体的类在触发事件。反过来,触发事件的类也不需要知道谁订阅了这些事件。
public class CookingProcess : IProgress
{
public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
public void UpdateProgress(float progress)
{
OnProgressChanged?.Invoke(this, new ProgressChangedEventArgs { ProgressNormalized = progress });
}
}
在上面的代码中,CookingProcess
只关心如何更新进度,而不需要知道谁在订阅它的进度事件。这样可以有效减少类之间的耦合。
4. 多态性和扩展性
接口允许以更灵活的方式扩展和替换实现类。如果希望换掉某个实现 IProgress
接口的类,只需要保证新的类实现了 IProgress
接口,而不需要修改其他使用该接口的代码。
例如,假设先使用了 CookingProcess
类,它触发了进度更新事件。如果想用一个新的类 LoadingProcess
替换它,只需要确保 LoadingProcess
也实现了 IProgress
接口。
public class LoadingProcess : IProgress
{
public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
public void UpdateProgress(float progress)
{
OnProgressChanged?.Invoke(this, new ProgressChangedEventArgs { ProgressNormalized = progress });
}
}
5. 支持不同对象使用同一个事件
正如我所提到的,接口让不同代码使用同一个事件。不同的类可以实现相同的事件接口,订阅方不需要知道具体的类类型,只需要关心接口定义。这使得系统可以灵活地组合和替换不同的组件。
小结
接口提供了:
- 统一的行为规范,让不同类遵循相同的协议;
- 解耦,通过接口订阅/发布事件,不需要直接依赖具体的类;
- 多态性和扩展性,通过实现接口,可以轻松替换不同的实现,而不需要修改其他代码;
- 事件处理的统一机制,让多个不同类的事件可以以相同的方式进行处理。