前沿资讯!Abstract Factory Pattern 抽象工厂模式简介与 C# 示例【创建型】【设计模式来了】 - 综合 -

当前位置:首页  >  综合  > 正文

前沿资讯!Abstract Factory Pattern 抽象工厂模式简介与 C# 示例【创建型】【设计模式来了】

前沿资讯!Abstract Factory Pattern 抽象工厂模式简介与 C# 示例【创建型】【设计模式来了】
2023-05-30 20:52:42 来源:博客园
〇、简介1、什么是抽象工厂模式?

一句话解释:


(资料图片)

通过对抽象类和抽象工厂的一组实现,独立出一系列新的操作,客户端无需了解其逻辑直接访问。

抽象工厂模式(Abstract Factory Pattern)是一种创建型模式。它用于创建一组相关对象的家族。强调的是一组对象之间的协作关系,而不是单个对象之间的依赖关系。抽象工厂类负责创建整个家族的对象的生命周期,并隐藏与实现有关的逻辑。

一个比喻:(科目与课代表)

语文和数学的课代表和副课代表,都按照抽象方法标准选好了,接下来同样的通过实现抽象类和接口标准,来选出两名物理课代表。当然,已经选出来的其他课代表,和本次选举无关联。

2、优缺点和使用场景

优点:

可以降低系统中各个对象之间的耦合度。隔离了具体类的生产,使得客户并不需要知道什么被创建。增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:

在增加新的产品方面比较困难,需要修改抽象工厂的接口,这样会导致所有的具体工厂也需要做出相应的修改。抽象程度高,可能会导致一些底层实现细节难以控制。

总之,抽象工厂模式能够有效地封装对象创建,但是扩展产品较为困难。它在软件开发中被广泛使用,特别是在跨平台软件开发中经常用到,使用时要注意系统对象的特点合理使用。

使用场景举例:

游戏开发:游戏中可能需要多种角色、武器、敌人等元素,它们之间可能存在关联性或依赖性,可以使用抽象工厂方法来快速构建游戏元素。数据库访问组件设计:不同数据库的连接、查询和数据存储方式可能存在差异,可以使用抽象工厂方法来创建不同数据库的访问组件、驱动和映射器。操作系统界面设计:不同操作系统的界面设计具有不同的特点,可以使用抽象工厂方法来创建不同操作系统下的控件。

总之,使用抽象工厂模式,都需要保证对象家族之间高内聚、松耦合,使得系统的设计和实现更加灵活和可扩展。

一、抽象工厂模式简单实现与扩展

通过两个抽象产品类 ProductA/ProductBBBB,实现四个具体产品类;在通过抽象工厂接口 IAbstractFactory,实现两个具体工厂的产品族 ConcreteFactory1/ConcreteFactory2。最后通过 Client 类注入工厂类的同时,创建产品的不同产品的实例,使客户端不用了解产品如何实例化,可以直接引用。

// 抽象产品类。public abstract class ProductA{    public abstract void OperationA();}public abstract class ProductBBBB{    public abstract void OperationBBBB();}// 具体产品类,其中 ProductA1、ProductA2、ProductB1 和 ProductB2 分别代表不同的产品。public class ProductA1 : ProductA{    public override void OperationA()    {        Console.WriteLine("ProductA1"s operation.");    }}public class ProductA2 : ProductA{    public override void OperationA()    {        Console.WriteLine("ProductA2"s operation.");    }}public class ProductBBBB1 : ProductBBBB{    public override void OperationBBBB()    {        Console.WriteLine("ProductBBBB1"s operation.");    }}public class ProductBBBB2 : ProductBBBB{    public override void OperationBBBB()    {        Console.WriteLine("ProductBBBB2"s operation.");    }}// 抽象工厂接口,定义了各种不同产品族的生产方法。public interface IAbstractFactory{    ProductA CreateProductA();    ProductBBBB CreateProductBBBB();}// 每个具体工厂都能够生产特定的产品族。public class ConcreteFactory1 : IAbstractFactory{    public ProductA CreateProductA()    {        return new ProductA1();    }    public ProductBBBB CreateProductBBBB()    {        return new ProductBBBB1();    }}public class ConcreteFactory2 : IAbstractFactory{    public ProductA CreateProductA()    {        return new ProductA2();    }    public ProductBBBB CreateProductBBBB()    {        return new ProductBBBB2();    }}// 客户端代码使用抽象工厂来创建各种不同产品族的产品,而无需关心它们的实际实现。public class Client{    private readonly ProductA _productA;    private readonly ProductBBBB _productBBBB;    public Client(IAbstractFactory factory)    {        _productA = factory.CreateProductA();        _productBBBB = factory.CreateProductBBBB();    }    public void Run()    {        _productA.OperationA();        _productBBBB.OperationBBBB();    }}
// 测试static void Main(string[] args){    Client client = new Client(new ConcreteFactory1());    client.Run();    Client client2 = new Client(new ConcreteFactory2());    client2.Run();        // 输出:    // ProductA1"s operation.    // ProductBBBB1"s operation.    // ProductA2"s operation.    // ProductBBBB2"s operation.}

下面我们尝试扩展出来一个新的产品 3:

// 具体产品类public class ProductA3 : ProductA{    public override void OperationA()    {        Console.WriteLine("ProductA3"s operation.");    }}public class ProductBBBB3 : ProductBBBB{    public override void OperationBBBB()    {        Console.WriteLine("ProductBBBB3"s operation.");    }}// 具体工厂都能够生产特定的产品族public class ConcreteFactory3 : IAbstractFactory{    public ProductA CreateProductA()    {        return new ProductA3();    }    public ProductBBBB CreateProductBBBB()    {        return new ProductBBBB3();    }}

测试:

static void Main(string[] args){    Client client = new Client(new ConcreteFactory1());    client.Run();    Client client2 = new Client(new ConcreteFactory2());    client2.Run();    Client client3 = new Client(new ConcreteFactory3());    client3.Run();}
二、抽象工厂模式在 .net 框架中的实际应用

例如 DbProviderFactory,这个类位于 System.Data.Common.dll 程序集中,该类扮演抽象工厂模式中抽象工厂的角色,源码如下:

// System.Data.Common, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a// System.Data.Common.DbProviderFactoryusing System.Data.Common;public abstract class DbProviderFactory{private bool? _canCreateDataAdapter;private bool? _canCreateCommandBuilder;public virtual bool CanCreateDataSourceEnumerator => false;public virtual bool CanCreateDataAdapter{get{if (!_canCreateDataAdapter.HasValue){using DbDataAdapter dbDataAdapter = CreateDataAdapter();_canCreateDataAdapter = dbDataAdapter != null;}return _canCreateDataAdapter.Value;}}public virtual bool CanCreateCommandBuilder{get{if (!_canCreateCommandBuilder.HasValue){using DbCommandBuilder dbCommandBuilder = CreateCommandBuilder();_canCreateCommandBuilder = dbCommandBuilder != null;}return _canCreateCommandBuilder.Value;}}public virtual DbCommand? CreateCommand(){return null;}public virtual DbCommandBuilder? CreateCommandBuilder(){return null;}public virtual DbConnection? CreateConnection(){return null;}public virtual DbConnectionStringBuilder? CreateConnectionStringBuilder(){return null;}public virtual DbDataAdapter? CreateDataAdapter(){return null;}public virtual DbParameter? CreateParameter(){return null;}public virtual DbDataSourceEnumerator? CreateDataSourceEnumerator(){return null;}}

下面是 SqlClientFactory.cs,继承了抽象类 DbProviderFactory,需要注意的是,此为引用程序集,即只包含元数据,不含可执行代码。如何通过工厂模式访问 SQLServer 数据库,可以参考官网示例:获取 DbProviderFactory

点击查看 SqlClientFactory.cs
// System.Data.SqlClient, Version=4.6.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a// System.Data.SqlClient.SqlClientFactoryusing System.Data.Common;using System.Data.SqlClient;/// Represents a set of methods for creating instances of the  provider"s implementation of the data source classes.public sealed class SqlClientFactory : DbProviderFactory{/// Gets an instance of the . This can be used to retrieve strongly typed data objects.public static readonly SqlClientFactory Instance;internal SqlClientFactory(){}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbCommand CreateCommand(){throw null;}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbCommandBuilder CreateCommandBuilder(){throw null;}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbConnection CreateConnection(){throw null;}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbConnectionStringBuilder CreateConnectionStringBuilder(){throw null;}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbDataAdapter CreateDataAdapter(){throw null;}/// Returns a strongly typed  instance./// A new strongly typed instance of .public override DbParameter CreateParameter(){throw null;}}

下面再看一下 Oracle 工厂的实现,完全独立于其他数据库的工厂:

点击查看源码 OracleClientFactory.cs
#region 程序集 Oracle.ManagedDataAccess, Version=4.122.21.1, Culture=neutral, PublicKeyToken=89b483f429c47342// C:\Users\zheng\.nuget\packages\oracle.manageddataaccess\21.10.0\lib\net462\Oracle.ManagedDataAccess.dll// Decompiled with ICSharpCode.Decompiler 7.1.0.6543#endregionusing System;using System.Data.Common;using System.Security;using System.Security.Permissions;using OracleInternal.Common;namespace Oracle.ManagedDataAccess.Client{    public sealed class OracleClientFactory : DbProviderFactory    {        public static readonly OracleClientFactory Instance = new OracleClientFactory();        public override bool CanCreateDataSourceEnumerator => true;        public override DbCommand CreateCommand()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand);            }            try            {                return new OracleCommand();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommand);                }            }        }        public override DbCommandBuilder CreateCommandBuilder()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder);            }            try            {                return new OracleCommandBuilder();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateCommandBuilder);                }            }        }        public override DbConnection CreateConnection()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection);            }            try            {                return new OracleConnection();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnection);                }            }        }        public override DbConnectionStringBuilder CreateConnectionStringBuilder()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder);            }            try            {                return new OracleConnectionStringBuilder();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateConnectionStringBuilder);                }            }        }        public override DbDataAdapter CreateDataAdapter()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter);            }            try            {                return new OracleDataAdapter();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataAdapter);                }            }        }        public override DbDataSourceEnumerator CreateDataSourceEnumerator()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator);            }            try            {                return new OracleDataSourceEnumerator();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateDataSourceEnumerator);                }            }        }        public override DbParameter CreateParameter()        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter);            }            try            {                return new OracleParameter();            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreateParameter);                }            }        }        public override CodeAccessPermission CreatePermission(PermissionState state)        {            if (ProviderConfig.m_bTraceLevelPublic)            {                Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission);            }            try            {                return new OraclePermission(state);            }            catch (Exception ex)            {                OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission, ex);                throw;            }            finally            {                if (ProviderConfig.m_bTraceLevelPublic)                {                    Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleClientFactory, OracleTraceFuncName.CreatePermission);                }            }        }    }}

当然,诸如 Mysql、DB2 等类同。由此可见,当后续新增数据库时,只需对 DbProviderFactory 抽象工厂进行继承即可,对已实现的数据工厂毫无影响。

标签:

(责任编辑:news01)
三国演义故事名称及内容_三国演义故事名称 世界讯息

三国演义故事名称及内容_三国演义故事名称 世界讯息

1、《三国演义》目录第一回宴桃园豪杰三结义斩黄巾英雄首立功第二回张翼德怒鞭督邮何国舅谋诛宦竖第三回议
05-29 02:03:35
正态分布概率密度公式怎么来的_正态分布概率密度公式

正态分布概率密度公式怎么来的_正态分布概率密度公式

1、好几个人独立研究的。2、比如,高斯,是从随机误差的分布规律这个角度发现了正态分布。3、而且他认为这
05-29 00:38:13
diesel 手表回收_diesel手表 短讯

diesel 手表回收_diesel手表 短讯

1、款式设计样式我都觉得很好埃。2、男款的一般表盘偏大,很有刚劲!很男人的款绝,设计一般稍有夸张,总体
05-28 22:37:19
世界信息:今日里包恩 头像(求里包恩的资料,带图片的)

世界信息:今日里包恩 头像(求里包恩的资料,带图片的)

里包恩头像,求里包恩的资料,带图片的很多人还不知道,现在让我们一起来看看吧!1、里包恩(Reborn)。本
05-28 21:29:50
雨未完,局地有大雨!体感清凉!

雨未完,局地有大雨!体感清凉!

昨天我省中南部全域和北部部分地区又被雨水洗礼有的地方强度还不小雨情回顾27日08时至28日08时:太原、阳泉
05-28 20:34:34
全球观焦点:在比尔森胜利1:0战胜布拉格斯巴达的比赛后,比尔...

全球观焦点:在比尔森胜利1:0战胜布拉格斯巴达的比赛后,比尔...

在比尔森胜利1:0战胜布拉格斯巴达的比赛后,比尔森主帅比莱克和球员维德拉接受了采访。维德拉表示:“目...
05-28 19:35:16
打造优质班主任队伍!广东省中小学德育研究与指导中心班主任工作部成立仪式在罗湖举办

打造优质班主任队伍!广东省中小学德育研究与指导中心班主任工作部成立仪式在罗湖举办

实现教育高质量发展,教师队伍是关键所在,而班主任队伍则是关键中的关键。
05-28 18:17:47
天天消息!美国6岁女孩疑遭虐死,邻居听到死前求饶

天天消息!美国6岁女孩疑遭虐死,邻居听到死前求饶

​本报昨天报道,布朗士一名6岁女孩被怀疑遭虐待致死,其母亲目前正受到讯问。在事发的几天里,涉事母亲才
05-28 17:32:16
锦城教务管理系统_锦城教务

锦城教务管理系统_锦城教务

1、同时登陆的人数太多,系统拥挤。2、到人数少点时自然能进去。本文到此分享完毕,希望对大家有所帮助。
05-28 16:21:59
焦点速递!宇特阀门有限公司

焦点速递!宇特阀门有限公司

1、宇特阀门有限公司于2017年08月24日成立。2、法定代表人张崇和,公司经营范围包括:铸造、制造、加工、销
05-28 15:17:55
温泉街道对涉氨制冷企业开展诊断式帮扶检查_当前滚动

温泉街道对涉氨制冷企业开展诊断式帮扶检查_当前滚动

大小新闻客户端5月26讯(通讯员:刘明辉)随着近期气温回升,涉氨制冷企业业务量随之上升,为有效防范化解
05-28 14:21:00
信息:汪小菲直播再爆粗口!台媒标题称其情绪失控“飙四字”

信息:汪小菲直播再爆粗口!台媒标题称其情绪失控“飙四字”

中时新闻网讯大S和汪小菲2021年11月正式宣布离婚,去年因大S闪电再婚具俊晔,和前夫因此闹翻,双方隔空交火
05-28 13:51:53
天天快播:圆珠笔油怎么擦掉_圆珠笔油如何清洗

天天快播:圆珠笔油怎么擦掉_圆珠笔油如何清洗

1、圆珠笔油渍:首先要看看衣服是什么料子,一般做法是在污渍处下面放一块毛巾,用小鬃刷沾上酒精顺丝轻轻
05-28 12:36:30
数说中国|4月份工业生产继续恢复 企业利润降幅继续收窄

数说中国|4月份工业生产继续恢复 企业利润降幅继续收窄

新华社北京5月27日电(记者谢希瑶、魏玉坤)记者27日从国家统计局获悉,4月份,工业生产继续恢复,企业营收
05-28 11:41:42
西安一培训机构突然关门 百余位家长退费难_天天时讯

西安一培训机构突然关门 百余位家长退费难_天天时讯

培训机构门前只贴了两张告示说资金链断裂,再无任何通知,要不是离职老师说机构倒闭了,等待复课的家长们还
05-28 11:03:48
天天要闻:幼童学动画从26楼跳下 事发时家没人:竟无生命危险 网友感慨太幸运

天天要闻:幼童学动画从26楼跳下 事发时家没人:竟无生命危险 网友感慨太幸运

5月26日,湖南吉首4岁半小朋友学动画片情节,用伞当降落伞从26楼跳下去,家里没有装防盗网(别处都装了防盗
05-28 09:46:43
@宝爸宝妈 童车安全不能大意 选购前要知道这些事

@宝爸宝妈 童车安全不能大意 选购前要知道这些事

“六一”儿童节到来之际,北京市市场监管局针对童车的选购,向消费者发出温馨提示。童车产品主要包括儿...
05-28 09:36:14
【天天快播报】官宣!丁俊晖将卸任球员董事会董事,中国一哥有望重回职业巅峰

【天天快播报】官宣!丁俊晖将卸任球员董事会董事,中国一哥有望重回职业巅峰

北京时间2023年5月28日,世界斯诺克巡回赛官方网站发布了一则重要通告。世界斯诺克巡回赛在通告中透露,中
05-28 06:49:07
稳了❓德甲冠军最新赔率:多特1.11大幅领先,拜仁6.5 热文

稳了❓德甲冠军最新赔率:多特1.11大幅领先,拜仁6.5 热文

稳了❓德甲冠军最新赔率:多特1 11大幅领先,拜仁6 5,德甲,拜仁,多特,直播
05-28 05:30:58
动车事故

动车事故

1、7·23甬温线特别重大铁路交通事故是2011年在浙江省发生的动车铁路交通事故。2、2011年7月23日20时30分0
05-28 03:50:53
2023高考语文万能答题模板 答题技巧

2023高考语文万能答题模板 答题技巧

其实2023高考语文答题也是有很多技巧的,高考语文能不能拿高分,就看同学们吃没吃透技巧了,那么2023高考语
05-28 02:37:38
武汉阳逻港物流有限公司(关于武汉阳逻港物流有限公司介绍)

武汉阳逻港物流有限公司(关于武汉阳逻港物流有限公司介绍)

1、武汉阳逻港物流有限公司于2007年12月25日成立。2、法定代表人谢炳木,公司经营范围包括:普通货运;货物
05-28 00:27:58
全球热文:使命召唤2pc单机版_使命召唤2中文单机版

全球热文:使命召唤2pc单机版_使命召唤2中文单机版

1、朋友我推荐你去游民星空网站去下载那上面的资源很多而且上面游戏更新的很快在这我就不给你连接了你在百
05-27 22:43:04
天天速讯:一试见分晓,满级豪华还得是奔驰

天天速讯:一试见分晓,满级豪华还得是奔驰

继纯电EQS、纯电EQE以及全新EQS纯电SUV之后,梅赛德斯-EQ旗下全新EQE纯电SUV于5月27日正式上市,共推出四款
05-27 21:15:30
什么是全日制本科助学(什么是全日制本科)

什么是全日制本科助学(什么是全日制本科)

1、全日制的是一种全天候教学的体制,是“全日制教育”的简称。2、意思是通过国家统一组织的每年6月(原...
05-27 20:12:10
李白思乡诗句有哪些_李白思乡的诗_今日讯

李白思乡诗句有哪些_李白思乡的诗_今日讯

1、1.《春夜洛城笛》【唐】李白谁家玉笛暗飞声,散入春风满洛城。2、此夜曲中闻折柳,何人不起故园情。3、
05-27 19:24:01
如何使用 Dremel 421 抛光剂_给男生送啥生日礼物合适 环球报道

如何使用 Dremel 421 抛光剂_给男生送啥生日礼物合适 环球报道

你需要的东西毛毡抛光布Dremel旋转工具抛光钻头Dremel以其轻便的手持式旋转工具而闻名。它们特别适用于家庭
05-27 18:18:36
百事通!彭州化工厂(关于彭州化工厂的基本详情介绍)

百事通!彭州化工厂(关于彭州化工厂的基本详情介绍)

彭州化工厂,州化工厂的基本详情介绍很多人还不知道,那么现在让我们一起来看看吧!1、成眉石化园区位于彭
05-27 17:03:09
impact是什么意思(关于impact是什么意思的基本详情介绍)_热推荐

impact是什么意思(关于impact是什么意思的基本详情介绍)_热推荐

impact是什么意思,mpact是什么意思的基本详情介绍很多人还不知道,那么现在让我们一起来看看吧!1、Impact是
05-27 16:08:49

为您推荐

精彩推送