3. 项目管理规范 && 命名方式规范
项目管理和命名方式都是“规范”的问题,部分公司会书写这方面的规范文档,以保证大家写出来的代码符合同一规范。这里只讨论常用的规范方式。
项目管理
项目管理这一块,涉及到解决方案中的各个概念,“解决方案”、“项目”、“文件夹”、“文件”(含说明性文档)。其中,解决方案包含项目,项目包含文件夹,文件夹包含文件。
解决方案 && 项目
一般来说一个解决方案是一个产品(网站、APP、桌面程序、类库等等)。
“项目”是一个个.csproj
,最简单的就是一个解决方案、一个项目,完成开发。但大家并不会这样做,我们会把一个项目拆分成多个项目,在不同的项目中完成不同的功能,然后通过引用
的方式完成不同项目之间的“粘连”。
为什么要这样做呢?一个原因是提高代码的可读性。比如最传统的三层架构,视图(eg.网站)是一个项目,业务逻辑(BLL)是一个项目,数据库操纵(DAL)是一个项目。将不同的职责放到不同的项目中,管理、优化起来更加方便,代码更加清晰。
另一个原因是,一个庞大的项目往往由多个开发组、多个开发者去开发和维护。将项目拆解成一个个独立的项目(甚至独立的解决方案),有利于多个开发者的工作。可能产品的部分功能由开发组A完成,另一部分功能由开发组B完成,而两个开发组的代码对对方来说都是不可见的。
所以“拆解”项目不要吝啬,根据业务、逻辑,最大化的去拆分项目,对未来产品的功能拓展和代码维护都有很大好处。
解决方案的命名一般和产品息息相关,比如Taobao、Wechat。而项目的命名,可以在解决方案的基础上加一些“说明”,比如Wechat.Mobile、Wechat.Tool,添加的内容概况项目的定位。
刚开始如果没有办法去很好的组织项目,可以参考其他优秀的项目,github上面有很多优秀的开源的项目,他们的项目是怎么组织的,代码是怎么写的,都非常值得学习,比如:vscode、aspnetboilerplate、Newtonsoft.Json。多看高Star的项目多学习。
对于产品名字过长的问题,在命名时,可以采用“缩写”。比如“aspnetboilerplate”,项目名都是“Abp.xxx.xxx”,用“Abp”代替了“aspnetboilerplate”。这都还不算很长的产品名称,业务系统很多都是“XXXXX系统”, 比如我之前开发的“生态监测管理系统”,英文翻译为“Ecological Monitoring Management System”,如果我们不进行缩写,解决方案的名称为“EcologicalMonitoringManagementSystem”,项目的名称是“EcologicalMonitoringManagementSystem.EntityFramework”,类名为“EcologicalMonitoringManagementSystem.EntityFramework.EntityMapper.Devices.DeviceCfg” ——————————哇,这简直是个灾难!又臭又长。
所以我们要进行缩写,将“Ecological Monitoring Management System”写成“EMMS”。
文件夹 && 文件
一个项目中可以有文件夹、文件,那它们又是怎样摆放的呢?我一般将程序的入口、配置的内容放在根目录,把“同一类型”的文件放在同一文件夹下。
比如控制台程序中的Program.cs、ASP .NET Core中的appsettings.json、Startup.cs,都适合放在项目的根目录。
同一类型、同一功能的内容,比如“完成发送短信的功能”、“定义产品表”,涉及的所有内容,可以放在同一文件夹中。
“定义产品表”的文件夹中,可以包含产品表的定义、产品表的MapperConfiguration、数据库Configuration、Dto等等。
“完成发送短信的功能”,可以包含发送短信的接口、发送短信的实现(多个短信服务平台)、发送短信的统一方法(包含业务逻辑)等。可能还会有一些README文件,用来备注一些关于发送短信信息。
文件夹的命名肯定以“贴近”其中的内容为目标。文件夹的命名的单复数,可以根据内容来判断:如果文件夹像一个工厂一样,整体完成一个零件,则可以取这个零件名称的单数形式作为文件夹的命名,比如“定义产品表”-Product;如果文件夹完成了同一目的的不同实现,则可以取复数,比如“完成发送短信的功能”-SendMessages。
一般一个.cs文件放一个类,类的命名为单数,且文件和类同名。有些开发喜欢把接口和实现类、类和它包含的枚举值放在一起,是个人习惯问题,我个人不推荐这样做。
一些同功能的类使用同样的命名结尾,比如“XXXBuilder”、“XXXConfiguration”、“XXXHelper”。
这里我展示一些我开发的项目中的文件摆放。
类、变量、方法命名方式
我们先直观感受下不同语言的命名方式:
java(来自Android开发之onClick事件的三种写法)
package a.a;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class AActivity extends Activity {
/** Called when the activity is first created. */
EditText Ev1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Ev1 = (EditText)findViewById(R.id.editText1);
//第一种方式
Button Btn1 = (Button)findViewById(R.id.button1);//获取按钮资源
Btn1.setOnClickListener(new Button.OnClickListener(){//创建监听
public void onClick(View v) {
String strTmp = "点击Button01";
Ev1.setText(strTmp);
}
});
//第二种方式
Button Btn2 = (Button) findViewById(R.id.button2);//获取按钮资源
Btn2.setOnClickListener(listener);//设置监听
}
Button.OnClickListener listener = new Button.OnClickListener(){//创建监听对象
public void onClick(View v){
String strTmp="点击Button02";
Ev1.setText(strTmp);
}
};
//第三种方式(Android1.6版本及以后的版本中提供了)
public void Btn3OnClick(View view){
String strTmp="点击Button03";
Ev1.setText(strTmp);
}
}
python
import requests #导入requests包
import json
def get_translate_date(word=None):
url = 'url'
From_data={}
#请求表单数据
response = requests.post(url,data=From_data)
#将Json格式字符串转字典
content = json.loads(response.text)
print(content)
#打印翻译后的数据
#print(content['translateResult'][0][0]['tgt'])
if __name__=='__main__':
get_translate_date('......')
C#(来自在C#中,Json的序列化和反序列化的几种方式总结)
Student stu = new Student()
{
ID = 1,
Name = "曹操",
Sex = "男",
Age = 1000
};
//序列化
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(Student));
MemoryStream msObj = new MemoryStream();
//将序列化之后的Json格式数据写入流中
js.WriteObject(msObj, stu);
msObj.Position = 0;
//从0这个位置开始读取流中的数据
StreamReader sr = new StreamReader(msObj, Encoding.UTF8);
string json = sr.ReadToEnd();
sr.Close();
msObj.Close();
Console.WriteLine(json);
//反序列化
string toDes = json;
//string to = "{\"ID\":\"1\",\"Name\":\"曹操\",\"Sex\":\"男\",\"Age\":\"1230\"}";
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(toDes)))
{
DataContractJsonSerializer deseralizer = new DataContractJsonSerializer(typeof(Student));
Student model = (Student)deseralizer.ReadObject(ms);// //反序列化ReadObject
Console.WriteLine("ID=" + model.ID);
Console.WriteLine("Name=" + model.Name);
Console.WriteLine("Age=" + model.Age);
Console.WriteLine("Sex=" + model.Sex);
}
Console.ReadKey();
可以看出,不同的语言写法差异很大,一方面是来自语法、格式本身不同,一方面是不同语言的开发者的命名规范不同。
C#的命名方式也有很多,这里我只描述我最常用的方式。
所有多单词的连接,都使用骆驼命名法
。即"Get Your Information",变为"GetYourInformation"。
类中的属性、方法名,以大写字母开头,字段以小写字母开头(一些开发喜欢以下划线开头)。
接口以“I”开头,比如“ISendMessage”。
命名方式,可以选择一种自己觉得最舒适的规范,然后一直遵守它。
举个例子:
public class StudentByINotifyPropertyChanged: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//实现INotifyPropertyChanged接口
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//私有字段小写
private string sex;
private string name;
//公开属性大写
public string Sex
{
get { return sex; }
set
{
sex = value;
NotifyPropertyChanged("Sex");
}
}
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
INotifyPropertyChanged
是接口,以“I”开头。“StudentByINotifyPropertyChanged”这个名字不是太好,实际上这是“StudentViewModel”(MVVM相关概念)。
私有字段小写开头,公有属性大写开头。如果将Name拆分为“姓”、“名”,则私有字段是“lastName”,“firstName”,公有属性为“LastName”、“FirstName”。
有方法“获得姓名”,则应该命名为“GetName”或者“GetFullName”。
学习技术最好的文档就是【官方文档】,没有之一。
还有学习资料【Microsoft Learn】、【CSharp Learn】、【My Note】。
如果,你认为阅读这篇博客让你有些收获,不妨点击一下右下角的【推荐】按钮。
如果,你希望更容易地发现我的新博客,不妨点击一下【关注】。