目前已经定义的接口如下

  • ICommand
  • ISystem
  • IModel

我们还需要两个接口,一个是表现层的IController,一个是工具层的IUtility。并且目前的ICommand的接口还不够完善,我们需要Command接口能通过单例的方式获取Architecture对象。

实现IController时出现的问题

IController与MVC中的Controller是一个意思。

表现层需要向底层发送Command,监听底层系统的事件,还有一个功能,就是查询Model或者System层的一些数据,这个功能我们用IController来定义。

在FrameworkDesign——Framework——Architecture中新建IController脚本

1
2
3
4
5
6
namespace FrameWorkDesign {
public interface IController : IBelongToArchitecture
{

}
}

由于表现层的对象时常进行创建和销毁,所以表现层的对象注册到Architecture是没有意义的,而这里定义IController接口的意义就是标记一下这个表现层的对象是属于表现层的。

所以我们修改一下表现层,修改一下CounterViewController脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using UnityEngine.UI;
using FrameWorkDesign;

namespace CounterApp
{
public class CounterViewController : MonoBehaviour , IController
{
//...
public IArchitecture Architecture { get; set; }//

private void Start()
{
m_CounterModel = Architecture.GetModel<ICounterModel>();//不使用CounterApp.Get
//...
}
//...
}
}

表现层调用的Architecture.GetModel是空的,因为我们不用CounterApp就意味着初始化的方法MakeSureArchitecture没有调用

所以修改Architecture脚本,使用单例的形式确保MakeSureArchitecture调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//...
public abstract class Architecture<T> : IArchitecture where T : Architecture<T>,new()
{
//...

public static IArchitecture Interface//添加代码
{
get
{
if(m_Architecture == null)
{
MakeSureArchitecture();
}

return m_Architecture;
}
}
//...
}

接着修改CounterViewController脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using UnityEngine.UI;
using FrameWorkDesign;

namespace CounterApp
{
public class CounterViewController : MonoBehaviour , IController
{
//...
public IArchitecture Architecture { get; set; } = CounterApp.Interface//

private void Start()
{
m_CounterModel = Architecture.GetModel<ICounterModel>();//不使用CounterApp.Get
//...
}
//...
}
}

这个时候Unity就会报错,原因是public IArchitecture Architecture { get; set; } = CounterApp.Interface这行代码在调用的时候会自然调用一些构造函数比如new PlayerPrefStorage,但是有一些构造函数使用了Unity自己的API(new PlayerPrefStorage就是),这是不允许的,Unity自己的API需要确保在Start或者Awake中初始化或者说调用,但是我们不能将它移动到Start方法里面,因为架构本身不能依赖MonoBehaviour的生命周期。

最简单的方法就是用.Net4.x提供的Lazy类,但为了我们的架构能够在比较老的Unity版本中运行,我们换一种方式解决这个问题。

拆分IBelongToArchitecture

先修改IBelongToArchitecture脚本

1
2
3
4
5
6
7
namespace FrameWorkDesign
{
public interface IBelongToArchitecture
{
IArchitecture GetArchitecture();
}
}

这样我们的Architecture的获取由原来的属性改为只能通过方法获取(get),而set部分我们使用一个新的接口

我们在FrameworkDesign——Framework——Architecture中新建ICanSetArchitecture脚本

1
2
3
4
5
6
7
namespace FrameWorkDesign
{
public interface ICanSetArchitecture
{
void SetArchitecture(IArchitecture architecture);
}
}

修改我们之前实现的IModelISystem,由于之前使用IArchitecture声明属性的时候,我们的用法就是在注册的时候给属性赋值,调用Init方法的时候从属性获取值,所以这里我们让这两个接口同时继承IBelongToArchitectureICanSetArchitecture

1
2
3
4
5
6
7
namespace FrameWorkDesign
{
public interface IModel : IBelongToArchitecture , ICanSetArchitecture
{
void Init();
}
}
1
2
3
4
5
6
7
namespace FrameWorkDesign
{
public interface ISystem : IBelongToArchitecture , ICanSetArchitecture
{
void Init();
}
}

修改Architecture中的RegisterModel<T>RegisterSystem<T>方法

1
2
3
4
5
6
7
8
9
10
public void RegisterModel<T>(T model) where T : IModel
{
model.SetArchitecture(this);
//...
}
public void RegisterSystem<T>(T system) where T : ISystem
{
system.SetArchitecture(this);
//...
}

修改IModel和ISystem实现的类,先修改CounterModel

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
public class CounterModel : ICounterModel
{
//删除掉IArchitecture属性引用
public void Init()
{
var storage = m_Architecture.GetUtility<IStorage>();
Count.Value = storage.LoadInt("COUNTER_COUNT", 0);
Count.OnValueChanged += count =>
{
storage.SaveInt("COUNTER_COUNT", count);
};
}

private IArchitecture m_Architecture;//添加
public IArchitecture GetArchitecture()//实现接口
{
return m_Architecture;
}

public void SetArchitecture(IArchitecture architecture)//实现接口
{
m_Architecture = architecture;
}

public BindableProperty<int> Count { get; } = new BindableProperty<int>()
{
Value = 0
};

}

再修改AchievementSystem

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
public class AchievementSystem : IAchievementSystem
{
private IArchitecture m_Architecture;//添加
public IArchitecture GetArchitecture()//实现接口
{
return m_Architecture;
}

public void SetArchitecture(IArchitecture architecture)//实现接口
{
m_Architecture = architecture;
}

public void Init()
{
var counterModel = m_Architecture.GetModel<ICounterModel>();

var previousCount = counterModel.Count.Value;

counterModel.Count.OnValueChanged += newCount =>
{
if(previousCount<10 && newCount >= 10)
{
Debug.Log("点击10次");
}else if(previousCount < 20 && newCount >= 20)
{
Debug.Log("点击20次");
}

previousCount = newCount;
};
}
}

最后修改我们之前实现IController的地方,也就是CounterViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CounterViewController : MonoBehaviour , IController
{
private ICounterModel m_CounterModel;//

//public IArchitecture Architecture { get; set; } = CounterApp.Interface; 删除掉

public IArchitecture GetArchitecture()
{
return CounterApp.Interface;
}

private void Start()
{
m_CounterModel = GetArchitecture().GetModel<ICounterModel>();//
//...
}
//...
}

我们可以看到,和之前声明属性不一样,我们这里实现public IArchitecture GetArchitecture()仅仅是一个方法,这个方法在Start里面调用,解决了之前的问题,而且我们把Get和Set分离成两个接口之后,IModel和ISystem只需要实现这两个接口就不会破坏之前的设计,而IController接口的实现只需要实现Get部分即可

用抽象类减少IModel和ISystem的样板代码

我们可以看到因为IModel和ISystem多实现了两个接口,总是要写这些接口就会造成样板代码,我们把Model和System改成抽象类来解决这个问题

修改IModel脚本

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
namespace FrameWorkDesign
{
public interface IModel : IBelongToArchitecture , ICanSetArchitecture
{
void Init();
}
public abstract class AbstractModel : IModel
{
private IArchitecture m_Architecture;
public IArchitecture GetArchitecture()
{
return m_Architecture;
}

public void SetArchitecture(IArchitecture architecture)
{
m_Architecture = architecture;
}

void IModel.Init() //接口阉割方法,防止子类调用
{
OnInit();
}

protected abstract void OnInit();
}
}

修改ISystem脚本

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
namespace FrameWorkDesign
{
public interface ISystem : IBelongToArchitecture , ICanSetArchitecture
{
void Init();
}
public abstract class AbstractSystem : ISystem
{
private IArchitecture m_Architecture;
public IArchitecture GetArchitecture()
{
return m_Architecture;
}

public void SetArchitecture(IArchitecture architecture)
{
m_Architecture = architecture;
}

void ISystem.Init() //接口阉割方法,防止子类调用
{
OnInit();
}

protected abstract void OnInit();
}
}

修改CounterModel,删除掉之前实现接口的地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CounterModel : AbstractModel,ICounterModel
{
protected override void OnInit()
{
var storage = GetArchitecture().GetUtility<IStorage>();//
Count.Value = storage.LoadInt("COUNTER_COUNT", 0);
Count.OnValueChanged += count =>
{
storage.SaveInt("COUNTER_COUNT", count);
};
}

public BindableProperty<int> Count { get; } = new BindableProperty<int>()
{
Value = 0
};

}

修改AchievementSystem,删除掉之前实现接口的地方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AchievementSystem : AbstractSystem, IAchievementSystem
{
protected override void OnInit()
{
var counterModel = GetArchitecture().GetModel<ICounterModel>();

var previousCount = counterModel.Count.Value;

counterModel.Count.OnValueChanged += newCount =>
{
if(previousCount<10 && newCount >= 10)
{
Debug.Log("点击10次");
}else if(previousCount < 20 && newCount >= 20)
{
Debug.Log("点击20次");
}

previousCount = newCount;
};
}
}