简介... 1

特点... 2

实际应用... 2

1、创建纯数据转基因对象... 3

2、属性修改通知... 3

3、数据注入... 4

4ORM... 5

a1条记录恢复为一个对象... 7

a1条记录恢复为一个对象附加映射... 8

b、多条记录恢复为1个对象... 8

c1条记录恢复为多个对象然后组合为一个主对象... 9

d、多条记录恢复为多个对象然后组合为一个主对象... 9

 

简介

  转基因工程在网络和民间引起了巨大的争议不是因为这项技术本身的原因,而是因为其推广手段涉嫌剥夺消费者的知情权,有强加于人的意图。对于转基因的研究全社会的态度基本是一边倒,都是支持的。

  这里的转基因和生物学没有一点关系,仅仅是感觉这个名字和框架的功能非常贴切,还有这个名字比较热络和夺人眼球。

 

  基因就是生物学上面的基本DNA片段,而DNA则基本相当于物理学上面的最小因子(分子或原子),也就是在生物学上面没有比DNA更小的因子了;而计算机广为人知的一个特点是其功能虽然强大,但是它却只能认识和处理0、1两个数字。其他所有的软件、图片、视频、声音等等文件,最后都要分解转换为0和1组成的数据才可以被计算机所处理。而我们熟知的各种编程语言如果没有编译器的编译、转换,计算机是不可能处理的。计算机语言的基本单位是语句,而语句在编译后就是一串01组成的计算机指令,在这个粒度上面,可以说语句就是软件的基因。而这里转基因中“转”字的第一个含义就是把一组语句转换一下位置;第二个含义就是把数据类型转变为统一的类型,方便管理。

  生物学上面的转基因通常仅仅转入很少的特定功能基因片段进入生物的生殖细胞(一般不会超过转入生物基因链长度的百万分之一,太长就会使得转基因个体的表现与原来大不相同,成为全新生物了),然后通过发育生长而成为转基因个体。而这里的转基因程度却是大刀阔斧,有些模块甚至转入了全部可执行代码,用户仅仅需要定义少部分的声明性语句支撑软件架构,然后调用少量的生成对象的语句就可以正常使用常见的功能了。

  和生物学的转基因隐喻一样,这里的“转”主要是转入,也就是把我们、第三方或者老版本软件提供的常用功能转入新版本的软件,达到充分使用可复用模块的目的,加速新版软件开发和定型的进程。

特点

  这个框架的特点主要有:

    1、入门门槛低,仅需要短时间的学习就可以入门;

    2、在代码规模非常小的情况下就可以提供非常强大的功能,方便学习和深入理解。千行规模的代码就可以提供普通纯数据对象的创建、属性更改通知、对象注入、数据库的持久化和恢复、处于骨架状态的软件立即执行和无缝使用第三方代码的注入等等常用的功能。不过这里有个状况需要说明,上述代码需要一款轻量级AOP框架辅助,我们这里使用的是Spring.NET;

    3、配置信息不需要XML文件,而是使用特性和接口代替。前者相当于弱类型编程,在拼错名称的情况下编译器不会报错,只有在运行时使用到的时候才会发现错误而发生异常,这样无疑拉长了错误反馈时间,增加了排除错误的难度;而后者属于强类型编程,在编译器的帮助下,出现拼写错误的时候根本不能通过编译,避免了大量低级错误的发生,也使得引入bug的几率大大降低;

    4、设计思路新颖,采用成熟技术,避免重复造轮子;

    5、结构扁平,各个模块之间基本是平行关系,阅读理解容易;

    6、应用广泛,不管是全新软件的开发,还是老软件的维护,都可以发挥作用。

    7、充分贯彻了依赖抽象而非具体实现的原则,使用这个框架后,模块之间的依赖关系全部转移到了框架和接口上面,各个模块也是通过接口进行通信。

    8、移植方便,整个框架的功能完全可以在JVM等具有反射调用功能的平台上面实现,这里选择c#完全是本人更擅长这种语言

  任何技术都有两面性,这里自然也不会有意外,因为很多功能需要大量使用反射调用实现和简化模型结构,因此这里抹杀了数据的个性,所有数据和对象统一按照object类型处理。这就意味着因为装箱、拆箱、反射调用等等原因,使得转基因对象的性能会有所下降。不过这与软件的发展趋势倒是非常吻合:以软件性能的有限下降换取软件开发复杂度的降低,以硬件性能的提升弥补软件性能的下降。另外因为框架比较新,基本没有经过实战检验,市场是否接受是个很大的考验。因为没有什么知名度,起名转基因也有傍名牌的意味。

 

  有些转基因产品虽然外表是宿主的样子,但是其内部已经完全和宿主没有关系了。比如有些转基因苹果外表和普通苹果无异,但是切开后发现里面的果肉却是猕猴桃。而我们的转基因对象这里也会存在类似的现象,而这里的外表无疑就是前面说的接口,而果肉则是调用接口成员时的执行代码了。

实际应用

  下面我们就演示利用1组接口、n行代码再加上必要的信息这些简单的素材创建的转基因对象,所具有的不可思议的功能:

  public interface IPerson {

    string Name { get; set; }

    int Age { get; set; }

  }

1、创建纯数据转基因对象

注意这里没有定义实现IPerson接口的类或者结构,仅仅定义了一个接口用于支持此对象的调用契约:对外承诺生成的对象有两个可读写的属性,而实现此接口的类则是由Sring框架动态生成。至于调用此对象成员时执行的代码,则是从我们的框架里面注入的。此时我们只需要如下原材料:

  1个接口IPerson;1行在InitPerson()里面的创建代码。

  [TestClass]

  public class TransGeneTest {

    [TestMethod]

    public void TestCreatePureDataPoco() {

      var poco = InitPerson();

      Assert.AreEqual(poco.Name, null);

      Assert.AreEqual(poco.Age, 0);

      poco.Name = "zhu";

      poco.Age = 43;

      Assert.AreEqual(poco.Name, "zhu");

      Assert.AreEqual(poco.Age, 43);

    }

 

    private IPerson InitPerson() {

      var poco = Factory.CreatePoco<IPerson>();

      return poco;

    }

  }

  2、属性修改通知

下面分别是修改前和修改后通知接口,当修改值与当前值一致时,不会触发修改通知。此时我们只需要如下原材料:

  3个接口IPerson,IChanged,IChanging;4行在InitPerson()里面的创建代码。

  作为演示,这里插入的是最简单的可供测试的代码,实际上完全可以插入验证之类的代码来实现基因(对象成员)层面上的加密解密功能。

  属性修改前通知,没有在此接口定义的成员修改前则不触发修改通知:

  public interface IChanging : INotifyPropertyChanging {

    int Age { set; }

  }

  属性修改后通知,没有在此接口定义的成员修改后则不触发修改通知:

  public interface IChanged : INotifyPropertyChanged {

    string Name { set; }

  }

  [TestClass]

  public class PropertyNotifyTest {

    [TestMethod]

    public void TestNotify() {

      var inst = InitPerson();

      var ced = counted;

      var cing = counting;

      inst.Name = "aa";

      Assert.AreEqual(ced, counted - 1);

      inst.Age = 100;

      Assert.AreEqual(cing, counting - 1);

      inst.Name = "aa";

      inst.Age = 100;

      Assert.AreEqual(ced, counted - 1);

      Assert.AreEqual(cing, counting - 1);

    }

 

    private IPerson InitPerson() {

      Type[] types = { typeof(IChanged), typeof(IChanging), typeof(IPerson) };

      var inst = Factory.CreatePoco(types) as IPerson;

      (inst as INotifyPropertyChanged).PropertyChanged = Changed;

      (inst as INotifyPropertyChanging).PropertyChanging = Changing;

      return inst;

    }

 

    private int counted = 0;

    private int counting = 0;

    void Changed(object obj, PropertyChangeEventArgs e) {

      counted++;

    }

    void Changing(object obj, PropertyChangeEventArgs e) {

      counting++;

    }

  }

  3、数据注入

注意下面的接口IPersonInfo的作用仅仅是注入信息的载体,类似于XML文件,为生成的转基因对象属性提供相应值。对其唯一的要求就是与前面主接口对应成员的类型和名称一致,,因此这两个接口完全可以放在不同的程序集里面。其读写权限则不作要求(不过因为生成的对象已经实现了配置信息的接口,因此其读写权限应该小于或等于主接口相应属性的读写权限)。此时我们只需要如下原材料:

  2个接口IPerson,IPersonInfo;3行在InitPerson()里面的创建代码。

 

  public interface IPersonV2 {

    string Name { get; set; }

    int Age { get; }

  }

  public interface IPersonInfo {

    [InjectInfo("zhu")]

    string Name { set; }

    [InjectInfo(44, true)]

    int Age { get; }

  }

  [TestClass]

  public class InjectTest {

    [TestMethod]

    public void TestInjectInfoToIPersion() {

      var person = InitPerson();

      Assert.AreEqual(person.Name, "zhu");

      Assert.AreEqual(person.Age, 44);

      person.Name = "li";

      Assert.AreEqual(person.Name, "li");

      person.Name = null;

      Assert.AreEqual(person.Name, null);

    }

 

    private IPersonV2 InitPerson() {

      Type[] types = { typeof(IPersonV2), typeof(IPersonInfo) };

      var intercept = Factory.GetSingleton<IPropertyInjectInterceptor>();

      var person = Factory.CreateAdapter(types, intercept) as IPersonV2;

      return person;

    }

  }

  4ORM

这里的赋值采用了全新的一次注入成型和智能装配技术,简化了对象初始化的过程。这里分别演示了1条数据库记录恢复为1个对象;1条记录恢复为n个对象,然后装配为一个对象;n条记录恢复为1个对象;n条记录恢复为m个对象,然后装配为一个对象这四种最常见的情形。

  下面为模拟从数据库读入的记录

  public class MockData {

    public static Dictionary<string, object> Record

    {

      get

      {

        var date = new Dictionary<string, object>();

        date.Add("Id", 1);

        date.Add("Name", "zhang san");

        date.Add("Birthday", "2000-1-20");

        date.Add("Phone", "020-21111111");

        date.Add("Address", "yuan cun");

        date.Add("City", "Guang zhou");

        return date;

      }

    }

 

    public static Dictionary<string, object> Record2

    {

      get

      {

        var date = new Dictionary<string, object>();

        date.Add("Id", 2);

        date.Add("Name", "li si");

        date.Add("Birthday", "2000-1-20");

        date.Add("Phone", "020-21111111");

        date.Add("Address", "yuan cun");

        date.Add("City", "Guang zhou");

        return date;

      }

    }

  }

  public static class MockDataV2 {

    public static Dictionary<string, object> Record1

    {

      get

      {

        var date = new Dictionary<string, object>();

        date.Add("Id", 1);

        return date;

      }

    }

    public static Dictionary<string, object> Record2

    {

      get

      {

        var date = new Dictionary<string, object>();

        date.Add("Id", 100);

        date.Add("Name", "zhang san");

        date.Add("Birthday", "2000-1-20");

        return date;

      }

    }

    public static Dictionary<string, object> Record3

    {

      get

      {

        var date = new Dictionary<string, object>();

        date.Add("Id", 1000);

        date.Add("Phone", "020-21111111");

        date.Add("Address", "yuan cun");

        date.Add("City", "Guang zhou");

        return date;

      }

    }

  }

  [TestClass]

  public class OrmTest {

    1To1、1Ton情形下主对象的接口

    public interface ICustomer {

      int Id { get; }

      string Name { get; }

      string Phone { get; }

      string BirthDay { get; }

      string City { get; }

      string Address { get; }

    }

 

    private IOrmInitilize ormInitilize = Factory.GetSingleton<IOrmInitilize>();

a1条记录恢复为一个对象

此时我们只需要如下原材料:

    1个接口;2行创建代码;1条模拟数据库记录。  

    [TestMethod]

    public void Test1To1() {

      ICustomer customer = ormInitilize.Init<ICustomer>(MockData.Record);

      TestCustomer(customer);

    }

 

    private void TestCustomer(ICustomer customer) {

      Assert.AreEqual(customer.Id, 1);

      Assert.IsNotNull(customer.Name);

      Assert.IsNotNull(customer.Address);

      Assert.IsNotNull(customer.City);

      Assert.IsNotNull(customer.Phone);

 

      Assert.IsNull(customer.BirthDay);

    }

    接口映射

    public interface ICustomerMap {

      特性传入的参数BirthDay与接口对应属性名称一致

      [Alias(nameof(IBaseInfo.BirthDay))]

      属性名称Birthday与数据库记录的字段名称一致

      string Birthday { get; }

    }

a1条记录恢复为一个对象附加映射

此时我们只需要如下原材料:

    2个接口;6行创建代码;1条模拟数据库记录。  

    [TestMethod]

    public void TestWithMap() {

      var ri = Factory.Create<IOrmInfo>();

      ri.Type = typeof (ICustomer);

      ri.TypeMap = typeof (ICustomerMap);

      ri.AddRecord(MockData.Record);

      ICustomer customer = (ICustomer)ormInitilize.Init(ri);

      Assert.IsNotNull(customer.BirthDay);

    }

b、多条记录恢复为1个对象

此时我们只需要如下原材料:

    1个接口;6行创建代码;3条模拟数据库记录。  

    [TestMethod]

    public void Test1ToMany() {

      var ri = Factory.Create<IOrmInfo>();

      ri.Type = typeof (ICustomer);

      ri.AddRecord(MockDataV2.Record1, MockDataV2.Record2, MockDataV2.Record3);

      var dict= ri.MergeDictionary();

      var customer = ormInitilize.Init<ICustomer>(dict);

      TestCustomer(customer);

    }

    nTo1、nTon情形下主对象的接口

    public interface ICustomerV2 {

      int Id { get; }

      IBaseInfo BaseInfo { get; }

      IAddressInfo AddressInfo { get; }

    }

    主对象属性类型接口

    public interface IAddressInfo {

      string Phone { get; }

      string City { get; }

      string Address { get; }

    }

    主对象属性类型接口  

    public interface IBaseInfo {

      string BirthDay { get; }

      string Birthday { get; }

      string Name { get; }

    }

c1条记录恢复为多个对象然后组合为一个主对象

此时我们只需要如下原材料:

    4个接口;4行创建代码;1条模拟数据库记录。  

    [TestMethod]

    public void TesstManyTo1() {

      var type = typeof(ICustomerMap);

      var data = MockData.Record;

      var customer = ormInitilize.Init<ICustomerV2>(data, type);

      Assert.IsNotNull(customer.AddressInfo);

      Assert.IsNotNull(customer.BaseInfo);

      Assert.IsNotNull(customer.Id);

      Assert.IsNotNull(customer.BaseInfo.BirthDay);

      Assert.AreEqual(customer.BaseInfo.BirthDay, customer.BaseInfo.Birthday);

    }

    public interface IShop {

      string Name { get; }

      List<ICustomerV2> Customers { get; }

    }

d、多条记录恢复为多个对象然后组合为一个主对象

此时我们只需要如下原材料:

    5个接口;9行创建代码;2条模拟数据库记录。  

    [TestMethod]

    public void TestManyToMany() {

      var shop = GetRestoreInfo();

 

      Assert.AreEqual(shop.Customers.Count(), 2);

      Assert.AreEqual(shop.Name, "zhang san");

      var first = shop.Customers.First(o => o.Id == 1);

      Assert.AreEqual(first.BaseInfo.Name, "zhang san");

    }

 

    private IShop GetRestoreInfo() {

      var coll = Factory .Create<IOrmInfo>();

      coll.Init(typeof (ICustomerV2), nameof(IShop.Customers), typeof (ICustomerMap));

      coll.AddRecord(MockData.Record, MockData.Record2);

 

      var master = Factory.Create<IOrmInfo>();

      master.Type = typeof (IShop);

      master.AddRestoreInfo(coll);

      master.AddRecord(MockData.Record);

      var shop = ormInitilize.Inits(master) as IShop;

      return shop;

    }

  }

  因为使用了全新的思路,加上Spring的相助,实现上述功能只使用了600行代码。而用户使用的时候代码的规模也大大减少,基本所有自动生成代码的场合在使用本框架后,都可以使用简单的接口代替。代码规模基本与存在的bug成正比,更少的代码也就意味着更少的bug。虽然这个框架目前非常粗鄙,功能也非常单薄,但是经过你我的细心雕琢和打磨,相信会成为下一代软件框架的标准。

}