part design_PET结构

part design_PET结构今天终于开始研究微软对于ASP.NET2.0的产品PetShop4.0了,这个产品从架构设计到编码,都有很多的想法值得去研究,而且此产品还引入了许多.net2.0的新特性。不过学习是个长期的过程,设计的思想不可能在段时间去领会,只能一个一个方面去学习和研究。今天研究了架构,遇到了不少问题,理解起来比较抽象,但还是有一点心得的。PetShop4.0采用了三层的架构,表现层、业务逻辑层和数据层。分

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用
今天终于开始研究微软对于ASP.NET2.0的产品PetShop4.0了,这个产品从架构设计到编码,都有很多的想法值得去研究 ,而且此产品还引入了许多.net2.0的新特性。不过学习是个长期的过程,设计的思想不可能在段时间去领会,只能一个一个方面去学习和研究。今天研究了 架构,遇到了不少问题,理解起来比较抽象,但还是有一点心得的。

PetShop4.0采用了三层的架构,表现层、业务逻辑层和数据层。

分层的优势:

1、使得各层相互独立,减少依赖性

2、方便开发人员职责分离,仅仅负责其中的某一块,而不用去考虑其它实现

3、方便管理和维护,其中一处的改动不会影响到其它的层

4、方便逻辑的复用

不足:

1、如果有新的功能加入到系统中,在自下而上的方法中,各个层都需要添加新的代码,小系统一般不会有太大的工作量,但是大系统往往比较麻烦

2、本来可以直接操作数据库完成对数据库的操作,但是由于分层,系统性能受到了一定的影响,对小的应用,还是使用不分层来实现对数据库的直接操作,可以取得较好的性能

3、分层之后,每层都有许多对应实现的模式,逻辑往往很抽象,给理解带来了困难,特别对于许多没有大型项目经验的人

整个系统的结构如下:

ps01.gif

系统架构简图

ps05.gif

详细的体系架构

表现层:系统的UI组件,直接面对使用者,主要负责最终用户跟系统的交互,包含基本的服务器端控件和跟页面有关的操作(js脚本)

业务逻辑层:是系统的核心层,所有的设计都是围绕该层进行设计,因为业务直接跟需求挂钩,比如用户的注册、登录等、用户订单的管理,宠物的管理等

数据访问层:进行数据存储并数据对象的持久化和各种操作(增、删、改、查),因为这层的设计比较重要,此层往往用工厂模式去实现支持不同的数据库,因而是理解架构的关键部分。

现从数据访问层入手,分析一下数据访问底层的设计:

ps06.gif

数据访问层的模块结构图

IDAL:抽象出数据访问的方法,要引用Model里面的实体对象

Model:包含实体对象

DBUtility:封装访问数据库的方法,针对不同的数据库有不同的实现(如SQLHelper.cs和OracleHelper.cs),是数据访问辅助工具

SQLHelper:封装ADO.NET的相关操作,用来优化操作

SQLServerDAL :以SQLServer的方式实现接口

OracleDAL:以Oracle的方式实现的接口

以ProductInfo为例,以SQLServer作为数据库例子:

ProductInfo类对产品进行操作(增、删、改、查),这些操作是通过DBUtility中的SQLHelper类来实现对数据库的操作的。

例如:

public static readonly string ConnectionStringLocalTransaction = ConfigurationManager.ConnectionStrings[“SQLConnString1”].ConnectionString;

public static readonly string ConnectionStringInventoryDistributedTransaction = ConfigurationManager.ConnectionStrings[“SQLConnString2”].ConnectionString;

完成在配置文件中读取相应的数据库连接字符串

然后再PetShop.SQLServerDAL 命名空间下的Product类中通过调用SQLHelper类中的方法完成对数据库的操作

例如:

        private const string SQL_SELECT_PRODUCTS_BY_CATEGORY = “SELECT Product.ProductId, Product.Name, Product.Descn, Product.Image, Product.CategoryId FROM Product WHERE Product.CategoryId = @Category”;

        private const string SQL_SELECT_PRODUCTS_BY_SEARCH1 = “SELECT ProductId, Name, Descn, Product.Image, Product.CategoryId FROM Product WHERE ((“;

  设置SQL命令,然后进行调用:

       public ProductInfo GetProduct(string productId) {

            ProductInfo product = null;

            SqlParameter parm = new SqlParameter(PARM_PRODUCTID, SqlDbType.VarChar, 10);

            parm.Value = productId;

            using (SqlDataReader rdr =
SqlHelper.ExecuteReader(
SqlHelper.ConnectionStringLocalTransaction, CommandType.Text, SQL_SELECT_PRODUCT, parm))

                if (rdr.Read())

                    product = new ProductInfo(rdr.GetString(0), rdr.GetString(1), rdr.GetString(2), rdr.GetString(3), rdr.GetString(4));

                else

                    product = new ProductInfo();

            return product;

        }

以上使用了
SqlHelper.ExecuteReader()方法通过操作ADO.NET实现对数据库记录的更新.

程序片段:

using PetShop.IDAL;

public class Product : IProduct{      }

说明Product 实现了在命名空间PetShop.IDAL中的IProduct接口,因此最后对数据的操作最终要返回一个Product对象,然后去调用该对象实现的方法.

PetShop.IDAL:数据访问层的接口

在此命名空间当中,定义了各种实体对象应该实现的接口,以下是定义在该命名空间中的IProduct接口。

using System;

using System.Collections.Generic;

using PetShop.Model;

namespace PetShop.IDAL{

   

    /// <summary>

    /// Interface for the Product DAL

    /// </summary>

    public interface IProduct{

   

        /// <summary>

        /// Method to search products by category name

        /// </summary>

        /// <param name=”category”>Name of the category to search by</param>

        /// <returns>Interface to Model Collection Generic of search results</returns>

        IList<ProductInfo>  GetProductsByCategory(string category);   

        /// <summary>

        /// Method to search products by a set of keyword

        /// </summary>

        /// <param name=”keywords”>An array of keywords to search by</param>

        /// <returns>Interface to Model Collection Generic of search results</returns>

        IList<ProductInfo>  GetProductsBySearch(string[] keywords);

        /// <summary>

        /// Query for a product

        /// </summary>

        /// <param name=”productId”>Product Id</param>

        /// <returns>Interface to Model ProductInfo for requested product</returns>

        ProductInfo  GetProduct(string productId);

    }

}

作为上层的BLL层,只管去调用这个接口,但是不管接口是怎么实现的,但是当BLL层调用的时候,通过的这个接口层最终返回给BLL层的是什么类型的对象呢?

在实现数据访问层的过程当中,使用了抽象工厂模式:
对于用户来说,工厂里产品如何生产的你不用知道,仅仅只去用工厂里生产出来的产品就可以了。

用工厂模式来实现了对SqlServerOracle数据库访问的操作,而BLL层不用知道也不用关心后台用的是哪一种数据库,它只要用接口就行了,接口中定义了要用的方法,当调用接口时会根据具体的情况再去调用底层数据访问操作。

DALFactory是关键,当BLL层要操作数据库时,DALFactory会根据具体情况再去使用SqlServerDALOracleDAL中的一个。这样系统上层只管调用,而下层实现细节。底下的数据层的实现细节对于上层来说被隐藏了。这样做到了分离的目标,尽量减少层与层之间的联系程度,保持各层的独立性。

DALFactory工厂模式是如何决定应该用SqlServerDAL还是用OracleDAL的呢?
以下是

DALFactory关于
IProduct接口
的实现代码:
using System.Reflection;
using System.Configuration;

namespace PetShop.DALFactory {

    /// <summary>
    /// This class is implemented following the Abstract Factory pattern to create the DAL implementation
    /// specified from the configuration file
    /// </summary>
    public sealed class DataAccess {

        // Look up the DAL implementation we should be using
        private static readonly string path = ConfigurationManager.AppSettings[“WebDAL”];
      
        //Return interface of
IProduct


        public static PetShop.IDAL.IProduct CreateProduct() {

            string className = path + “.Product”;
            return (PetShop.IDAL.IProduct)Assembly.Load(path).CreateInstance(className);
        }
    }
}
在web.config里面读取的配置文件(该配置文件设置了用户使用的数据库的类型):
    <appSettings>
        <!– Pet Shop DAL configuration settings. Possible values: PetShop.SQLServerDAL for                     SqlServer, PetShop.OracleServerDALfor Oracle. –>
        <add key=”WebDAL” value=”PetShop.SQLServerDAL”/>
        <add key=”OrdersDAL” value=”PetShop.SQLServerDAL”/>
        <add key=”ProfileDAL” value=”PetShop.SQLProfileDAL”/>
   

<appSettings>
在上面的程序片段中,


private static readonly string path = ConfigurationManager.AppSettings[“WebDAL”];



该句用来在配置文件中读取
AppSettings
节点的值,该配置文件说明了系统应该使用的是那一个数据库(SQLServer或者Oracle或者其它),读取的过程是通过读取key,然后的到该key所对应的value。

该句:

public static PetShop.IDAL.IProduct CreateProduct() {

            string className = path + “.Product”;
            return (PetShop.IDAL.IProduct)Assembly.Load(path).CreateInstance(className);
        }
是问题的关键之处,通过该方法最终返回了

IProduct接口类型。
但是

该句 string className = path + “.Product”; 返回了
PetShop.SQLServerDAL.Product对象
然后使用:

Assembly.Load(
PetShop.SQLServerDAL
).CreateInstance(

PetShop.SQLServerDAL.Product
);利用反射特性动态加载

PetShop.SQLServerDAL.dll,同时创建了
PetShop.SQLServerDAL.Product
对象的实例,最终
以接口PetShop.IDAL.
IProduct
类型返回。
当BLL层调用数据访问层的时候,通过IDAL接口使用了

类PetShop.SQLServerDAL.Product,进而去使用了该类的代码,本质还是调用了对象的实例,不像简单的New一个对象的实例,如果使用
Product aProduct=new Product(); 则表示没有完全将对象完全分离,对象和对象的实例化有着太强的联系。而使用返回接口的方法,很好的将实例化和对象之间的关系弱化,达到了较好的分离。

然后看一下BLL层是如何调用IDAL层的:
BLL层的代码:
using System.Collections.Generic;
using PetShop.Model;
using PetShop.IDAL;

namespace PetShop.BLL {

    /// <summary>
    /// A business component to manage products
    /// </summary>
    public class Product {

        // Get an instance of the Product DAL using the DALFactory
        // Making this static will cache the DAL instance after the initial load
        private static readonly IProduct dal = PetShop.DALFactory.DataAccess.CreateProduct();   
        /// <summary>
        /// Query for a product
        /// </summary>
        /// <param name=”productId”>Product Id</param>
        /// <returns>ProductInfo object for requested product</returns>
        public ProductInfo GetProduct(string productId) {

           
            // Return empty product if the string is empty
            if(string.IsNullOrEmpty(productId))
                return new ProductInfo();

            // Get the product from the data store
            return dal.GetProduct(productId);
        }
    }
}
使用该句:

private static readonly IProduct dal = PetShop.DALFactory.DataAccess.CreateProduct();
使用工厂得到

Product DAL的一个实例化的对象,然后通过该对象去调用相应的方法,如下:


dal.GetProduct(productId);
这样,BLL层就可以直接调用DAL层的接口完成对数据库的操作,但是BLL层并不知道它操作的数据库是那个数据库,而这些都是由DAL Factory去实现的,因此BLL层只管去调用接口,而对底层访问数据库的实现细节一概不知,如果BLL层有较大的变动,基本是不会影响到DAL层的具 体实现的,即使底层有变动,也很少会影响到上一层的业务细节,因此,层与层之间得到了较好的分离。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183601.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mybatis log plugin2022激活码【2022.01最新】2022.02.21[通俗易懂]

    (mybatis log plugin2022激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.cn/100143.htmlHC…

  • win-64位系统 plsql 登录 出现ORA12154问题

    win-64位系统 plsql 登录 出现ORA12154问题ora12154,ora28547

  • python基础(3)列表list[通俗易懂]

    python基础(3)列表list[通俗易懂]列表列表特点:是一种序列结构,与元组不同,列表具有可变性,可以追加、插入、删除、替换列表中的元素新增元素appendappend添加一个对象,可以是任意类型a=['zhangsa

  • RESTful API 设计指南

    RESTful API 设计指南

  • 地形——高度图

    地形——高度图在游戏中,我们将采用高度图来模拟现实生活中的丘陵和山谷。高度图其实就是一个数组,而该数组的每个元素都指定了地形方格中某一个顶点的高度值。线框模式普通地形模式在高度图中,通常为其每一个元素只分配一个字节的存储空间,以至于高度能在区间[0,255]内取值。但在实际应用中,为了匹配3D世界的尺度,可能要对高度值进行比例变换,就很可能超出上述区间范围。因此,可以分配一个整型或浮点型数组来存储这些高…

  • Java线程(二):线程同步synchronized和volatile

    Java线程(二):线程同步synchronized和volatile要说明线程同步问题首先要说明Java线程的两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。拿上篇博文中的例子来说明,在多个线程之间共享了Count类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存Count对象的一个副本,当线程操作Count对象时,首先从主内存复制Co…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号