Unity3d 一个优秀的程序必备的几种设计模式
unity编程众所周知,它是属于脚本化,脚本没有一个具体的概念跟架构, 导致在项目过程中,经常出现哪里需要实现什么功能,就随便添加脚本, 结果,就造成了一片混乱,不好管理。 更有甚者,自己的写的代码闲置一段时间后,再去想找某个功能的实现,都要在视图中翻来覆去找半天。 哎!请容许我在此感叹一声,这还是你写的东西么? 因此,一个好的设计模式是多么的重要啊, 那么,我们在使用unity3d开发东西的时候,脚本架构到底应该如何来写? 呵呵... 其实,我也给不了你们具体答案,因为每个人的开发习惯,每个团队的开发模式也各有千秋, so,在此我只做几种设计模式的总结, 主要参考书籍有《设计模式》《设计模式之禅》《大话设计模式》以及网上一些零散的文章, 但主要内容还是我本人的一些经验以及感悟。 写出来的目的一方面是系统地整理一下,一方面也与广大的网友分享, 至于你们到底如何使用, 望君斟酌啊! [C#] 纯文本查看 复制代码
class Animal { public void breathe(string animal) { Debug.Log(animal+"呼吸空气"); } } public class Client { Animal animal = new Animal(); void Start() { animal.breathe("牛"); animal.breathe("羊"); animal.breathe("猪"); } } 运行结果: [C#] 纯文本查看 复制代码
class Terrestrial { public void breathe(String animal){ Debug.Log(animal + "呼吸空气"); } } class Aquatic { public void breathe(String animal){ Debug.Log(animal + "呼吸水"); } } public class Client { public static void main(String[] args) { Terrestrial terrestrial = new Terrestrial(); Debug.Log(terrestrial.breathe("牛")); Debug.Log(terrestrial.breathe("羊")); Debug.Log(terrestrial.breathe("猪")); Aquatic aquatic = new Aquatic(); Debug.Log( aquatic.breathe("鱼")); } }
运行结果: [C#] 纯文本查看 复制代码
class Animal { public void breathe(String animal) { if("鱼".equals(animal)) { Debug.Log((animal+"呼吸水")); } else { Debug.Log((animal+"呼吸空气")); } } } public class Client { public static void main(String[] args) { Animal animal = new Animal(); Debug.Log(animal.breathe("牛")); Debug.Log(animal.breathe("羊")); Debug.Log(animal.breathe("猪")); Debug.Log(animal.breathe("鱼")); } }
可以看到,这种修改方式要简单的多。 [C#] 纯文本查看 复制代码
class Animal { public void breathe(String animal){ Debug.Log(animal+"呼吸空气"); } public void breathe2(String animal){ Debug.Log(animal+"呼吸水"); } } public class Client { public static void main(String[] args) { Animal animal = new Animal(); Debug.Log(animal.breathe("牛")); Debug.Log(animal.breathe("羊")); Debug.Log(animal.breathe("猪")); Debug.Log(animal.breathe2("鱼")); } } 可以看到,这种修改方式没有改动原来的方法,而是在类中新加了一个方法,这样虽然也违背了单一职责原则, 遵循单一职责原的优点有:
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。 设计模式六大原则(2):里氏替换原则 肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑。 [C#] 纯文本查看 复制代码
class A
{
public int func1(int a, int b)
{
return a - b;
}
}
public class Client
{
void Start()
{
A a = new A();
Debug.Log(("100-50="+a.func1(100, 50));
Debug.Log(("100-80="+a.func1(100, 80)));
}
}
运行结果: [C#] 纯文本查看 复制代码
class B:A{
public int func1(int a, int b){
return a+b;
}
public int func2(int a, int b){
return func1(a,b)+100;
}
}
public class Client{
void Start()
{
B b = new B();
Debug.Log("100-50="+b.func1(100, 50));
Debug.Log("100-80="+b.func1(100, 80));
Debug.Log("100+20+100="+b.func2(100, 20));
}
类B完成后,运行结果: 本帖隐藏的内容以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。 依赖倒置原则的核心思想是面向接口编程,我们依旧用一个例子来说明面向接口编程比相对于面向实现编程好在什么地方。 [C#] 纯文本查看 复制代码
class Book{ public String getContent(){ return "很久很久以前有一个阿拉伯的故事……"; } } class Mother{ public void narrate(Book book){ Debug.Log("妈妈开始讲故事"); Debug.Log(book.getContent()); } } public class Client{ void Start() { Mother mother = new Mother(); Debug.Log(mother.narrate(new Book())); } } 运行结果: [C#] 纯文本查看 复制代码
class Newspaper{ public String getContent(){ return "林书豪38+7领导尼克斯击败湖人……"; } } 这位母亲却办不到,因为她居然不会读报纸上的故事,这太荒唐了,只是将书换成报纸,居然必须要修改Mother才能读。 [C#] 纯文本查看 复制代码
interface IReader{ public String getContent(); } Mother类与接口IReader发生依赖关系,而Book和Newspaper都属于读物的范畴, [C#] 纯文本查看 复制代码
class Newspaper : IReader { public String getContent(){ return "林书豪17+9助尼克斯击败老鹰……"; } } class Book : IReader{ public String getContent(){ return "很久很久以前有一个阿拉伯的故事……"; } } class Mother{ public void narrate(IReader reader){ Debug.Log("妈妈开始讲故事"); Debug.Log(reader.getContent()); } } public class Client{ public static void main(String[] args){ Mother mother = new Mother(); Debug.Log(mother.narrate(new Book())); Debug.Log(mother.narrate(new Newspaper())); } } 运行结果: 采用依赖倒置原则给多人并行开发带来了极大的便利, 设计模式六大原则(4):接口隔离原则 (图1 未遵循接口隔离原则的设计) [C#] 纯文本查看 复制代码
interface I { public void method1(); public void method2(); public void method3(); public void method4(); public void method5(); } class A{ public void depend1(I i){ i.method1(); } public void depend2(I i){ i.method2(); } public void depend3(I i){ i.method3(); } } class B : I{ public void method1() { Debug.Log("类B实现接口I的方法1"); } public void method2() { Debug.Log("类B实现接口I的方法2"); } public void method3() { Debug.Log("类B实现接口I的方法3"); } //对于类B来说,method4和method5不是必需的,但是由于接口A中有这两个方法, //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。 public void method4() {} public void method5() {} } class C{ public void depend1(I i){ i.method1(); } public void depend2(I i){ i.method4(); } public void depend3(I i){ i.method5(); } } class D : I{ public void method1() { Debug.Log("类D实现接口I的方法1"); } //对于类D来说,method2和method3不是必需的,但是由于接口A中有这两个方法, //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。 public void method2() {} public void method3() {} public void method4() { Debug.Log("类D实现接口I的方法4"); } public void method5() { Debug.Log("类D实现接口I的方法5"); } } public class Client{ void Start(){ A a = new A(); Debug.Log(a.depend1(new B())); Debug.Log(a.depend2(new B())); Debug.Log(a.depend3(new B())); C c = new C(); Debug.Log(c.depend1(new D())); Debug.Log(c.depend2(new D())); Debug.Log(c.depend3(new D())); } } 可以看到,如果接口过于臃肿,只要接口中出现的方法,不管对依赖于它的类有没有用处,实现类中都必须去实现这些方法,这显然不是好的设计。 (图2 遵循接口隔离原则的设计) [C#] 纯文本查看 复制代码
interface I1 { public void method1(); } interface I2 { public void method2(); public void method3(); } interface I3 { public void method4(); public void method5(); } class A{ public void depend1(I1 i){ i.method1(); } public void depend2(I2 i){ i.method2(); } public void depend3(I2 i){ i.method3(); } } class B : I1, I2{ public void method1() { Debug.Log("类B实现接口I1的方法1"); } public void method2() { Debug.Log("类B实现接口I2的方法2"); } public void method3() { Debug.Log("类B实现接口I2的方法3"); } } class C{ public void depend1(I1 i){ i.method1(); } public void depend2(I3 i){ i.method4(); } public void depend3(I3 i){ i.method5(); } } class D : I1, I3{ public void method1() { Debug.Log("类D实现接口I1的方法1"); } public void method4() { Debug.Log("类D实现接口I3的方法4"); } public void method5() { Debug.Log("类D实现接口I3的方法5"); } } 接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。 说到这里,很多人会觉的接口隔离原则跟之前的单一职责原则很相似,其实不然。 运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这一原则。 设计模式六大原则(5):迪米特法则 自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。 迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。 [C#] 纯文本查看 复制代码
//总公司员工 class Employee{ private String id; public void setId(String id){ this.id = id; } public String getId(){ return id; } } //分公司员工 class SubEmployee{ private String id; public void setId(String id){ this.id = id; } public String getId(){ return id; } } class SubCompanyManager{ public List<SubEmployee> getAllEmployee(){ List<SubEmployee> list = new List<SubEmployee>(); for(int i=0; i<100; i++){ SubEmployee emp = new SubEmployee(); //为分公司人员按顺序分配一个ID emp.setId("分公司"+i); list.Add(emp); } return list; } } class CompanyManager{ public List<Employee> getAllEmployee(){ List<Employee> list = new List<Employee>(); for(int i=0; i<30; i++){ Employee emp = new Employee(); //为总公司人员按顺序分配一个ID emp.setId("总公司"+i); list.Add(emp); } return list; } public void printAllEmployee(SubCompanyManager sub){ List<SubEmployee> list1 = sub.getAllEmployee(); foreach (SubEmployee e in list1) { Debug.Log(e.getId()); } List<Employee> list2 = this.getAllEmployee(); foreach (Employee e in list2) { Debug.Log(e.getId()); } } } public class Client{ void Start(){ CompanyManager e = new CompanyManager(); Debug.Log(e.printAllEmployee(new SubCompanyManager())); } } 现在这个设计的主要问题出在CompanyManager中,根据迪米特法则,只与直接的朋友发生通信, [C#] 纯文本查看 复制代码
class SubCompanyManager{ public List<SubEmployee> getAllEmployee(){ List<SubEmployee> list = new List<SubEmployee>(); for(int i=0; i<100; i++){ SubEmployee emp = new SubEmployee(); //为分公司人员按顺序分配一个ID emp.setId("分公司"+i); list.Add(emp); } return list; } public void printEmployee(){ List<SubEmployee> list = this.getAllEmployee(); foreach (SubEmployee e in list) { Debug.Log(e.getId()); } } } class CompanyManager{ public List<Employee> getAllEmployee(){ List<Employee> list = new List<Employee>(); for(int i=0; i<30; i++){ Employee emp = new Employee(); //为总公司人员按顺序分配一个ID emp.setId("总公司"+i); list.Add(emp); } return list; } public void printAllEmployee(SubCompanyManager sub){ sub.printEmployee(); List<Employee> list2 = this.getAllEmployee(); foreach (Employee e in list2) { Debug.Log(e.getId()); } } } 修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。 迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。 设计模式六大原则(6):开闭原则 闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了, 在仔细思考以及仔细阅读很多设计模式的文章后,终于对开闭原则有了一点认识。 其实,开闭原则无非就是想表达这样一层意思:用抽象构建框架,用实现扩展细节。 最后说明一下如何去遵守这六个原则。 |
-
UKZ{8@UJF(VW~)K051A_5DV.jpg (29.64 KB, 下载次数: 17)
-
@7(F]%{X@YSPTZ9WWB$7290.jpg (31.71 KB, 下载次数: 12)
- 下一篇:怎么写出一本程序员风格的修真小说?
- 上一篇:AssetBundle资源打包