程序员头痛起名根治指南
作者:marinewu,腾讯PCG客户端开发工程师
导语基于Readability的出题阅卷经验的一些不完全的具体的命名指南。
ThereareonlytwohardthingsinComputerScience:cacheinvalidationandnamingthings。
PhilKarlton
软件开发中一个著名的反直觉就是起名儿这个看上去很平凡的任务实际上很有难度。身边统计学显示,越是有经验的程序员,越为起名头痛。给小孩起名儿都没这么费劲,对吧,王浩然?
命名的困难可能来自于以下几个方面:信息压缩:命名的本质是把类方法的信息提炼成一个或几个词汇,这本身需要对抽象模型的准确理解和概括。预测未来:类方法的职责可能会在未来有变化,现在起的名字需要考虑未来可能的变动。语言能力:缺少正确的语法知识,或是缺少足够的词汇量。本来英文就不是大部分中国人的母语,更甚者,计算机的词汇表不同于日常交流词汇表,有大量黑话。不良设计:混乱的职责分布、不清晰的抽象分层、错误的实现,都会导致无法起出好的名字。在这个意义上,起名字其实是对设计的测试:如果起不出名字来,很可能是设计没做好重新想想设计吧。
命名就像写作,会写字不等于会写作。而且,命名更多像是一门艺术〔注〕(此处艺术的含义取自于Knuth命名会诉诸品味和个人判断。),不存在一个可复制的命名操作手册。
本文描述一些实用主义的、可操作的、基于经验的命名指南,并提供了一个代码词汇表,和部分近义词辨析。本文没有涉及讨论名字的形而上学,例如如何做更好的设计和抽象以利于命名,也没有涉及如何划分对象等,也无意讨论分析哲学。命名原则
命名是一门平衡准确性和简洁性的艺术名字应该包含足够的信息能够表达完整的含义,又应该不包含冗余的信息。准确Precision
名字最重要的属性是准确。名字应该告诉用户这个对象方法的意图它是什么和它能做什么。事实上,它是体现意图的第一媒介名字无法表现含义时读者才会阅读文档。
名字应该是有信息量的、无歧义的。以下一些策略可以增加名字的准确度:可读
最基本的语法原理,是一个类(ClassRecordStruct。。。随你喜欢)应该是一个名词,作为主语。一个方法应该是动词,作为谓语。换言之,类是什么,方法做什么,它们应该是可读的,应该是〔Object〕〔Does。。。〕式的句子。
可读是字面意思,意味着它应该是通顺的,所以应该:避免API中使用缩写
就像是给老板的汇报中不会把商业计划写成BusiPlan一样,也不应该在公开API中使用一些奇怪的缩写。现在已经不是1970年了,没有一个方法不能超过8个字符的限制。把类方法的名字写全,对读者好一点,可以降低自己被同事打一顿的风险。
creat是个错误,是个错误,是个错误!
但是,首字母缩略词的术语是可行并且推荐的,如Http,Id,Url。
以下是可用的、得到普遍认可的缩写:configurationconfigidentifieridspecificationspecstatisticsstatsdatabasedb(onlycommoninGo)regularexpressionreregexregexp
未得到普遍认可的缩写:requestreqresponseresprspservicesvrobjectobjmetadatametabusinessbusi
reqrespsvr在服务名称中很常见。这非常糟糕。请使用全称。
再次说明:以上的说明是针对API名称,具体包括公开对象函数名字、RPCWebAPI名字。在局部变量使用缩写不受此影响。避免双关
对类方法的命名,不要使用2表示To,4表示For。funcfoo2Bar(fFoo)BarBADfuncfooToBar(fFoo)BarGOODfuncto(fFoo)BarGoodifnotambiguous。
24这种一般只有在大小写不敏感的场合才会使用,例如包名e2e比endtoend更可读。能区分大小写的场合,不要使用24。合乎语法
虽然不能完全符合语法(例如通常会省略冠词),但是,方法的命名应该尽量符合语法。例如:classCar{voidtireReplace(Tiretire);BAD,readslikeCarstirereplacesvoidreplaceTire(Tiretire);GOOD,readslikereplacecarstire}
关于命名的语法见语法规则一章。使用单一的概念命名
命名本质上是分类(taxonomy)。即,选择一个单一的分类,能够包含类的全部信息,作为名字。
考虑以下的角度:
例如,把大象装进冰箱,需要有三步打冰箱门打开,把大象放进去,把冰箱门关上。但是,这可以用单一的概念来描述:放置。classFridge{publicvoidopenDoorAndMoveObjectIntoFridgeAndCloseDoor(Elephantelphant);BADpublicvoidput(Elephantelphant);GOOD}应该使用所允许的最细粒度的分类
避免使用过于宽泛的类别。例如,这世界上所有的对象都是对象,但显然,应该使用能够完整描述对象的、最细颗粒度的类别。classFridge{publicput(Elephantelephant);GOOD。publicdealWith(Elephantelephant);BAD:dealwith?Anythingcanbedealtwith。How?}
简而言之,名字应该是包含所有概念的分类的下确界。简洁Simplicity
名字长通常会包含更多信息,可以更准确地表意。但是,过长的名字会影响可读性。例如,王浩然是一个比浩然达拉崩吧斑得贝迪卜多比鲁翁米娅莫拉苏娜丹尼谢莉红迪菲特(defeat)昆图库塔卡提考特苏瓦西拉松蒙达鲁克硫斯伯古比奇巴勒王可能更好的名字。(来自于达啦崩吧)
在此,我提出一个可能会有争议的观点:所有的编程语言的命名风格应该是趋同的。不同于通常认为Java的命名会倾向于详尽,Go的命名会倾向于精简,所有的语言对具体的名字到底有多长的建议应该是几乎一样的对外可见应该更详细,内部成员应该更精简。具体地:public,如public类的名字、public方法的名字应该详细、不使用缩写、减少依赖上下文。通常是完整名词短语。nonpublic,如类成员、私有方法不使用缩写、可以省略上下文。下界是单词,不应该使用单字符。local,如函数的局部变量基本上是风格是自由的。不影响可读性的前提下,例如函数方法长度很短,可以使用单字符指代成员。
上述规则像是Go的风格指南。但是,并没有规定Java不能这样做。事实上,Java的冗长是Java程序员的自我束缚。即使在Java的代码里,也可以这样写:publicclassBazelRuntime{publicbooleanexec(Commandcmd){Stringmcmd。mainCommand();YES,youcanusesinglelettervariablesinJava。。。。}}
同样,在Go的代码中也不应该出现大量的无意义的缩写,尤其是导出的结构体和方法。typestructRuntime{}packagenameisbazel,sobazelprefixisunnecessarytypestructRtm{}BAD。DONOTINVENTABBREVIATION!
当然,由于语言特性,在命名风格上可能会有差异。例如,由于Go的导入结构体都需要加包前缀,所以结构名中通常不会重复包前缀;但CJava通常不会依赖包名。但是,上述的原则仍然是成立的可见度越高,应该越少依赖上下文,并且命名越详尽。
GoogleGoStyleGuide是唯一详尽讨论命名长度的风格指南,非常值得参考,并且不限于Go编程:https:google。github。iostyleguidegodecisionsvariablenames一致Consistency
另一个容易被忽略的命名的黄金原则是一致性。换言之,名字的选取,在项目中应该保持一致。遵守代码规范,避免这方面的主观能动性,方便别人阅读代码。通常情况下,一个差的、但是达成共识的代码规范,也会远好于几个好的、但是被未达成共识的规范。
这个图我能用到下辈子:〔xkcd927〕()
但是仅符合代码规范是不够的。如同所有的语言,同一个概念,有多个正确的写法。
考虑以下的例子:messageRecord{int32starttimemillis1;OKint32commitedat2;Wait。Whynotcommittime?Anythingspecial?int32updatetime3;Whatunit?Alsomillis?google。types。Timestampendtime4;WTF?Whyonlyendtimeistyped?}
几种都是合理的(虽然不带单位值得商榷)。但是,如果在一个代码中出现了多种风格,使用起来很难预测。您也不想使用这样的API吧?
所以,在修改代码的时候,应该查看上下文,选择已有的处理方案。一致性大于其它要求,即使旧有的方案不是最好的,在做局部修改时,也应该保持一致。
另一个可考虑的建议是项目的技术负责人应该为项目准备项目的专有词汇表。语法规则类类型类
类应该是名词形式,通常由单个名词或名词短语组成。其中,主要名词会作为名词短语的末尾。例如Thread,PriorityQueue,MergeRequestRepository。名词短语通常不使用所有格。如,并非ServiceOfBook,也不是BooksService(省略),而是BookService。接口
接口的命名规则和类相同。除此之外,当接口表示可行动类型时,可使用另一个语法,即Verbable。例如:publicinterfaceSerializable{byte〔〕serialize();}publicinterfaceCopyableT{Tcopy();}publicinterfaceClosable{voidclose();}
(Go通常不使用这个命令风格。只在JavaC中使用。)辅助类
只在Java(注1)中使用。一个类或概念所有的辅助方法应该聚合在同一个辅助类。这个类应该以被辅助类的复数形式出现。不推荐使用HelperUtils后缀表示辅助类。尤其不推荐使用UtilsHelpers做类名,把所有的辅助方法包进去。如:classCollections{}ForCollectionclassStrings{}ForStringclassBaseRuleClasses{}ForBaseRuleClassclassStringUtils{}WORSE!classStringHelper{}WORSE!
注1:客观来说,这适用于所有强制OOP的语言(所有强制把方法放在类里的语言)。但是除了Java,没有别的语言这么烦啦。方法
方法通常是谓语(动词),或是谓宾(动词名词)结构。注意以上语法中,动词都在最前端。例如:classExpander{Stringexpand(Stringattribute);主谓StringexpandAndTokenizeList(Stringattribute,ListStringvalues);主谓宾}
除此之外,有以下特例值得注意:访问器Getter
直接使用所Get的对象的名词形式,即Foo()。不要使用GetFoo()。
Java:所有的Getter都需要一个get前缀是来自于过时的JavaBeansSpecification,以及Javaer的思想钢印。funcCounts()int;GOODfuncGetCounts()int;BAD:UNNECESSARY。断言Predicate
断言函数指返回结果是布尔型(即真伪值)的函数。它们通常有以下命名格式:系动词:主系表
即isAdjective()或areAdjective()格式,表示是否具有某个二元属性。类似于Getter,可以省略系语,只使用表语,即:adjective()。funcIsDone()bool{}OKish。Butcouldbebetter。funcDone()bool{}GOOD。Whybotherwithisare?funcCheckEnabled()bool{BAD。Nobodycaresifitischecked。Justtelltheuserifitisenabled。returnenabled;}funcEnabled()bool{}GOOD。情态动词:主助谓谓(宾表)
情态动词也是常见的断言形式。常见的是以下三个:should:查询当前是否应该执行给定的实义动词。can:查询当前类所在状态是否可以执行给定的实义动词。某些情况下,也可以使用第三人称单数作为更简洁的代替。must:特殊形式。不同于前两者,会执行给定的实义动词。must表示执行必须成功,否则会抛出不可恢复错误(throwpanic)。类似于C中常见的OrDie后缀。funcCompile(sstring)Regexp,errorReturnserroruponfailurefuncMustCompile(sstring)RegexpPanicsuponfailurefunc(rRegexp)CanExpand(sstring)boolWhethersislegalandcanbeexpandedfunc(rRegexp)Expands(sstring)boolWhetherrexpandss,i。e。rcanexpands。func(rRegexp)ShouldReset()boolWhetherthestaterequiresreset。Doesnotperformdefactoreset。func(rRegexp)Reset()Defactoreset。表尝试:主maybetry谓(宾表)
上文must的反面,表示尝试性的执行,并且失败不会造成严重后果:maybe前缀用以表示指定的行为有前置条件,也在方法中执行。如果前置条件不满足,不会执行指定行为。通常不会出现在公开API。try通常用于TryParsePattern,用于避免抛出异常。voidmaybeExecute(){if(!predicate()){return;}execute}std::uniqueptrDateTimeParseOrDie(std::stringviewdateTime);boolTryParse(stringviewdateTime,DateTimedateTime);第三人称单数
另一个常见场景是我们希望表示类拥有某些属性,但是使用助动词并不合适。如果前文描述,常见的选择是使用第三人称单数的静态动词(Stativeverb)(注1)表示类满足给定断言。func(lList)Contains(einterface{})boolfunc(rRegexp)Expands(sstring)bool
注1:简单地说,静态动词是表示状态的动词,与动态动词(Dynamicverb)表示动作对应。或言持续性动词。一阶逻辑Firstorderlogic,PredicateLogic
一阶逻辑量词也是常见的前缀:all表示所有对象满足给定要求any表示任意对象满足给定要求none表示没有任何对象满足给定要求
语法:一阶量词动词形容词classStream{Returnswhetherallelementsofthisstreammatchtheprovidedpredicate。booleanallMatch(Predicatelt;?superTp);Returnswhetheranyelementsofthisstreammatchtheprovidedpredicate。booleananyMatch(Predicatelt;?superTp);Returnswhethernoelementsofthisstreammatchtheprovidedpredicate。booleannoneMatch(Predicatelt;?superTpredicate)}介词
介词经常与某些动词固定搭配,因此,通常可以省略动词,而只使用介词作为方法名称。to:转换至另一对象,等价于convertTo。to会产生一个全新的对象,通常不持有对原对象的引用。as:返回某个视图,等价于returnViewAs。一个视图(View)通常是对原有对象的另一角度的抽象,通常会持有对原有数据的引用,而不会产生新的数据。offromwith:构造新对象,等价于createOutOfcreateFromcreateWith。见下文工厂模式。on:监听事件,等价于actUpon。见下文事件。classFoo{publicListTtoList();Convertto(Constructanewinstanceof)anewList。Createsanewlist。publicListTasList();ReturnaListasadifferentview。Holdsreferenceoftheoriginalreference。staticFooof();ConstructFooasafactorymethod。staticFoofrom(Bar);ConstructFoofromBar。Foowith(Bar);ConstructanewFoobyreplacingBarwithnewBar。voidonClick(ClickEvente);Actuponclickevent。}
参考资料:https:testing。googleblog。com201710codehealthidentifiernamingpostforworl。htmlhttps:journal。stuffwithstuff。com20160616longnamesarelonghttps:journal。stuffwithstuff。com20090605namingthingsincodehttps:habr。comenpost567870namesinengineeringhttps:medium。comwixengineeringnamingconvention8basicrulesforanypieceofcodec4c5f65b0c09https:github。comkettanaitonamingcheatsheet〔〔EffectiveJava〕〕Item68:Adheretogenerallyacceptednamingconventions词汇表
下文按用途归类了常见动词和名词,并对同义近义词进行了辨析。类名词类继承
AbstractBase
Impl
DefaultJavaInterface:通常不需要额外的表示。不要加I前缀,或后缀FooInterface。Abstractclass:通常会添加AbstractBase前缀以明确属性。这是因为InterfaceImpl是常见的,Class也是常见的,但是基于继承的抽象类是特殊的、应该予以避免的,应该给予特殊标记。Implementation:如果不实现接口,通常不需要任何特殊修饰符。如果以isa的方式实现了某个接口,那么通常实现会以{InterfaceName}Impl的方式命名。如果一个类实现了多个接口,那么通常这个类应该是以作为主要目标的接口为基础命名。例如classBazelBuilderImplimplementsBazelBuilder,AutoClosable,Serializable。如果一个接口有多个实现,通常会基于它们本身的实现特点命名,并且不使用Impl后缀。Default通常用来命名默认的实现,即其它实现如果不存在会fallback到的实现。如果所有的实现都是平等地位,那么不要使用Default命名。https:github。combazelbuildbazelwithsomefakeexamplespublicinterfaceSkyFunction{}publicabstractclassAbstractFileChainUniquenessFunctionimplementsSkyFunction{}publicclassDefaultSkyFunctionimplementsSkyFunction{}publicclassBazelModuleInspectorFunctionimplementsSkyFunction{}publicinterfaceVisibilityProvider{}publicfinalclassVisibilityProviderImpl{}CC的interface是通过抽象类不存在基类成员变量模拟。通常接口所有的成员函数都是公开纯虚函数。使用Impl表示实现。Abstractclass:通常会添加Base后缀以明确属性。这是因为InterfaceImpl是常见的,Class也是常见的,但是基于继承的抽象类是特殊的、应该予以避免的,应该给予特殊标记。levelDBincludesdb。hclassDB{public:virtualDB();MUST!virtualStatusDelete(constWriteOptions,constSlice)0;}dbdbimpl。hclassDBImpl:publicDB{}rocksDBBaseclassclassCacheShardBase{}GoGo的interface从来不是用来做isa定义的。Go的interface契约通过ducktyping满足。interface应该在消费方定义,而非提供方。因此,interfaceFoostructFooImpl不应该出现。Go也并没有抽象类,虽然可以将一个结构体嵌入到另一个结构体中。所以BaseAbstract也极少出现。原则上,Go的类关系更为简化,命名更强调意义优先,因此在命名时避免使用修饰性前后缀。异常
ExceptionErrorJava
所有的异常扩展应该以Exception为后缀。所有的错误应该以Error为后缀。对异常和错误的区别请参见https:docs。oracle。comjavase7docsapijavalangThrowable。htmlpublicclassIllegalArgumentException;publicclassOutOfMemoryError;C
C的exception通常指语法特性,与throw对应,而error可以用来表示具体的异常错误。stdlibstd::exception;std::runtimeerrorGo
所有的错误都是error。因此,所有自定义的对error的扩展都以Error作为后缀。os。PathError测试
Test
JavaGoC均使用Test作为测试类的后缀。模块
ModuleComponent
ModuleComponent通常会在框架中使用。不同的语言框架对于ModuleComponent有不同的定义。在非框架代码中应该减少使用ModuleComponenet等命名,因为可能与已有框架冲突,并且ModuleComponenet过于宽泛而缺少实质意义。
ModuleComponent是意义相近的词,都可以表示模块或者组件。两者虽然有细微的分别,但是框架通常都显式(即在文档中指定,或者通过框架语义约束)地把它们定义为框架语境下的某些结构层级。
总结,ModuleComponent命名应该注意:只应该在框架代码中使用。ModuleComponent应该在框架的语境中给出确切的定义。服务
Service
Service通常用于作为CS架构下的服务提供者的名称的后缀,如:HelloService
但除此之外,Service可以表示任何长期存活的、提供功能的组件。例如:BackgroundServiceAndroid后台服务ExecutorService线程池执行服务,也是服务
BAD:不要使用Svr缩写。使用全称。容器
HolderContainerWrapper
HolderContainerWrapper都表示容器,具有同一个意图:为一个类增加额外的数据功能,例如:添加某些语境下的元数据(Decorator模式)做适配以在另一个环境中使用(Adapter模式)
通常的结构如下:classObjectHolder{privatefinalObjectobject;otherstuff。。。publicObjectobject(){}Othermethods}
这三个词没有区别。在同一个项目中,应该保持一致。控制类
ManagerController
Manager和Controller是同义词。它们通常用来表示专门控制某些类的类。
这两个词有以下几个常见场景:Manager管理资源,如DownloadManager,PackageManager。Manager通常特指管理对象的生命周期,从创建到销毁。Controller通常在某些架构中,尤其是MVC(ModelViewController)。
即使如此,ManagerController是无意义词汇,出现时充满了可疑的味道类应该管理它们自己。ControllerManager多了一层抽象,而这很可能是多余的。认真考虑是否需要ManagerController。辅助类
UtilUtilityUtilsHelper{ClassName}s
辅助类是强制OOP的语言(i。e。Java)所需要的特殊类。通常它们是一些辅助方法的合集。Java
将与某个类型相关的辅助方法放在一个类中,并且以复数形式命名辅助类。如:JavastdlibpublicfinalclassStrings{}publicfinalclassLists{}publicfinalclassCollections{}
避免使用UtilUtilityUtilsHelper。它们是无意义词汇。C
使用全局方法。如果担心命名污染,将之置入更细粒度的namespace。Go
使用全局方法。函数式
FunctionPredicateCallback
Function通常表示任意函数。Predicate表示命题,即通常返回类型为bool。Callback指回调函数,指将函数作为参数传递到其它代码的某段代码的引用。换言之,Function可以作为Callback使用。因此,Callback在现代函数式编程概念流行后,通常很少使用。Java
熟悉Java8开始提供的函数式语义。如果可以使用标准库的语义,不要自己创建名词。注意Function指单入参、单出参的函数。如果是多入参的函数,直接定义FunctionalInterface并且按用途命名,例如OnClickListener。listen(Context,ClickEvent)。java。util。functionPredicateTf(T)boolFunctionT,Rf(T)RConsumerTf(T)voidSupplierTf()TC
firstclass函数的标准类型为std::function。
C表示命名函数对象的惯用法是fun。Stdlib中会缩写function为fun,如pmemfunref,因此与stdlib一致,在代码中不要使用fn或是func。Go
Go通常使用Func或是Fn表示函数类型。typeProviderFuncfunc(configConfigSource,sourcePassPhraseSource)(Provider,error)typecancelFnfunc(context。Context)error
在同一个项目中,应该保持一致。
作为参数时,函数不会特意标明Fn,而是遵从普通的参数命名方式:funcSort(lessfunc(a,bstring)int)
换言之,函数是一等公民。设计模式类
类方法通常都按它们的行为模式来命名。恰好,设计模式就归类抽象了很多行为模式。所以设计模式提供了很多好的名字。创建式
Factory:工厂模式。通常,使用工厂方法即可,不需要一个额外的工厂类。只有当工厂特别复杂,或者工厂有状态时再考虑使用工厂类。
Builder:构建者模式。一般来说Builder都是作为innerclass,如classFoo{staticclassFooBuilder{}}行为式Adapter:适配器
在GoF中Adapter本来是将一个类封装以可以被作为另一个类型被调用,这样调用方不需要额外改变代码。这种用途通常被内化到容器上,见上文〔容器类〕部分。
在现代,Adapter更多地被作为数据类数据类的转化,如常见的pbpb:classProtoAdapterS,TextendsMessage{}Decorator:装饰器
在GoF中Decorator本来是将一个类作为抽象类,通过组合继承实现添加功能。实际上现代的编程实践中往往通过直接提供一个容器的封装提供装饰功能,见上文〔容器类〕部分。所以GoF式Decorator并不常见,除非像Python在语法层面提供了装饰器。在Java中类似的功能是注解。Delegation:委派模式
GoF中是非常基本的模式:由一个类负责接受请求,并把请求转发到合适的实例类中执行。classRealPrinter{}classPrinter{RealPrinterprinter;}
Delegate非常常见,也提供了两个名字,请注意区分:Delegate是被委任的对象。Delegator是委任对象。
所以,通常情况下Delegator在命名中会更常见,类似于Dispatcher。Delegate更多作为一个类型或是接口被实现。具体的选择参见〔编排〕部分。Facade:外观模式
GoF中FacadePattern通常是指为子系统提供一个更高层的统一界面,屏蔽子系统的独有的细节。在现实中,Facade通常用来为非常复杂的类系统定义一个较为简化的界面,如:proto,extremelycomplicatedTripResponsemessageTripResponse{。。。。。。stringlastmessage3279;}classTripResponseFacade{privatefinalTripResponseresponse;Triptrip();Endpointsource();AbstractedandprocessedEndpointtarget();Abstractedandprocessed}
Facade与Adapter的主要区别在于Facade的主要目的是为了简化,或者说更高层次的抽象,并且通常简化的界面不服务于专门的对接类。Adapter通常是为了一个特定的对接类实现。
注意Facade命名通常可以省略。仅当你的意图是明确告知用户这是关于某个类的外观时使用。Proxy:代理模式
GoF中代理模式用来添加一层抽象,以对实际类进行控制,并添加某些行为(如lazymemoized),或是隐藏某些信息(例如可见性或是执行远程调用)。
Proxy与Facade的区别在于Proxy通常是为了额外的控制记录等行为,而非只是为了更高的抽象简化。
注意Proxy作为代码模式时,通常不应该出现在命名之中。使用具体的Proxy的目的作为命名,如LazyCar或是TracedRuntime,而非CarProxy或是RuntimeProxy。
Proxy还有另一个含义就是真正的代理,如代理服务器。在这种情况下,使用Proxy是合适且应该的。这也是另一个为什么代理模式不应该用Proxy命名的原因。Iterator:迭代器
时至今日仍然最常见的模式之一。Interator有以下两个术语,不要混淆:Iterable:迭代容器Iterator:迭代器Visitor:访问者模式
访问者模式用来遍历一个结构内的多个对象。对象提供accept(Visitor)方法,调用Visitor。visit方法。
即使如此,Visitor应该并不常见,因为它可以简单地被函数式的写法替换:classCar{voidaccept(ConsumerCarvisitor);NolongerneedtodefineVisitorclass。}ObserverObservable:观察者模式
ObserverPublisherSubscriberProducerConsumer
时至今日最常见的模式之一。和事件驱动编程(Eventbased)有紧密关系Oberservable发布消息,所有注册的Obeserver会接收消息。PublisherSubscriber也是类似的,它们的区别在于Observer模式往往是强绑定的注册和分发通常在Observable类中实现;而PubSub模式通常有专门的MessageBroker,即Publisher与Subscriber是完全解耦的。
PubSub与ProducerConsumer的区别是:PublisherSubscriber:在事件流系统中,表示1:N广播订阅。ProducerConsumer:在整个流系统中,专指1:1生产消费。ProducerConsumer也是PubSub系统的组件(广播也是一对一广播的)。有些系统(Kafka)使用ConsumerGroup表示Subscriber。
所有的消息注册的模式由三部分组成:Notification:消息本身Sender:消息发送者注册Receiver:消息接收者
关于命名参见〔事件〕部分。Strategy:策略模式
StrategyPolicy
策略模式在GoF中用以指定某个行为在不同场景下的不同实现,作为策略。
Strategy模式往往不是很显式。现代通常使用Strategy表示实际的策略,即对信息不同的处理策略,而不采取Strategy模式的含义。
在策略这个语义中,StrategyPolicy没有区别。在同一个项目中,应该保持一致。Command:命令模式
命令模式在GoF中以类开代表实际行动,将行动封装,以支持重复、取消等操作。
Command在现代编程实践中可以通过简单的函数式方案替换,如:FunctionT,Tcommand;Javastd::functionconstT(constT)command;CtypeCommandfunc(T)TGo
现代通常使用Command表示实际的命令,而不采取Command模式的含义。NullObject模式
Tombstone
NullObject模式不在GoF当中。它是一个用来代替null的object,对其所有的操作都会被吞掉。NullObject主要是为了避免空指针。合理的零值,例如gotime。Time0,也可以理解为一种NullObject。
通常会有一个专门的对象表示NullObject。可以借用Tombstone表示NullObject。ObjectPool对象池模式
Pool
对象池模式不在GoF当中。它是将一系列昂贵的对象创建好放在一个池子中,并使用户通过向池子申请对象,而不再自己手动地创建销毁对象。最著名的池化的例子是线程池,即ThreadPool。
Pool通常用来表示对象池子,例如ThreadPool,ConnectionPool。
Arena
Arena是指Regionbasedmemorymanagement,是指一片连续的内存空间,用户在其中分配创建对象,管理内存。前后缀并发异步
Concurrent
Synchronized
Async
有时候我们需要特别标明一个类是线程安全的。通常这是特意为了与另一个线程不安全的实现做区分。典型的例子是HashMap和ConcurrentHashMap。如果一个类只是单纯是线程安全的,那么通常不需要在名字里特意说明,在文档里说明即可。
例如:Thisclassisdesignedtobethreadsafe。classSomeClassThreadSafe{}Thisclassisimmutablethusthreadsafe。classSomeClassImmutable{}
Concurrent通常是用来说明该类是线程安全的前缀。Synchronized是另一个在Java中可用的标明类是线程安全的前缀。但是,这通常说明这个类是通过synchronized机制来保证线程安全的,所以只在Java中使用。
另一个常见的场景是同一个方法有两种实现:同步阻塞和异步不阻塞的。在这种情况下,通常会命名异常不阻塞的方法为{synchronizedMethod}Async,例如:publicTexec();publicFutureTexecAsync();
如果一个异步的方法并没有对应的同步方法,通常不需要加Async后缀。
在Go中,如果一个方法是意图在其它协程中异步执行,不需要加Async后缀。缓存惰性
CachedBuffered
Lazy
Memoized
名词辨析:Cached表示获取的对象会被缓存,保留一段时间,在缓存期间不会重新获取。Buffered与Cached同义。Lazy表示这个对象会被在第一次调用时创建,之后一直保留Memoized通常表示执行结果会在第一次计算后被记忆,之后不会再重复计算
注意Buffered不应该与Buffer混淆。Buffer作为名词专指缓冲区。
注意Cached不应该与Cache混淆。Cache作为名词专指缓存。
CachedBuffered应该在项目中是一致的。CachedLazyMemoized取决于对象是被获取的,还是创建的,还是计算获得的。不可变性
Mutable
Immutable
Mutable显式地声明一个类是可变的,Immutable显式地声明一个类是不可变的。通常情况下,类似于并发安全性,是否可变应该在类文档中说明,而不应该在类名中,显得臃肿。只有当一个类同时有可变不可变版本时,可以使用ClassImmutableClass。存储数据处理数据类
Object
Data
Value
Record
Entity
Instance
上面几个都可以用来表示一个表示数据的类。但是这些词是典型的无意义词汇,如果把它们从名字中删除,仍然可以表示完整意义,那么应该删掉。classCarObject{}BadclassCarEntity{}BadclassCarInstance{}BadclassCar{}GoodclassMapKey{}classMapValue{}OK。Couldntbeshortened。classLoggingMetricsData{}BadclassLoggingMetricsValue{}BadclassLoggingMetricsRecord{}BadclassLoggingMetrics{}GoodclassDrivingRecord{}OK。Couldntbeshortened。
StatisticsStats
表示统计数据。Stats是公认的可用的Statistics的缩写,JavaCGo均可。存储
Storage
Database
Store
DB
Cache
Verbs:
savestoreput
StorageDatabaseStoreDB都可以作为存储服务,即广义上的数据库(不是必须是完整的DBMS)。其中,在CGo中DB是常见且可接受的。在Java中通常使用全称。
项目内应该选择一个术语保持一致。
savestoreput在数据库类中是同义词。同一个项目中应该保持一致。数据格式
Schema
Index
Format
Pattern
名词辨析:Schema借用数据库的概念,指数据的结构模式。Index借用数据库的概念,专指数据的索引。FormatPattern通常是泛指的模式格式概念。实际出现时,FormatPattern往往和字符串相关,如Java使用Pattern表示正则表达式。在非公共代码中,FormatPattern通常过于宽泛,应该考虑选用更细化的名词。哈希
HashDigestFingerprintChecksum
HashDigest哈希是一种将任何数据映射到一个较小的空间的方法。映射通常被称为哈希函数(HashFunction),映射值通常被称为摘要(Digest)。Hash(Data)Digest
Checksum出自编码论,可以理解为一种特殊的哈希函数,用来检查文件的完整性。换言之,如果一份数据出现了任何变动,Checksum应该期待会改变。(但是Checksum实际上并不要求唯一性,见Fingerpint)
Fingerprint类似于Checksum,但是Fingerprint通常更严格,它通常要求最少有64bit,使得任何两个文件只要不同,几乎(概率意义上接近264)不可能有同一份指纹,即唯一性。(但是Fingerprint的定义不要求密码安全性即cryptographic)
所以Checksum只是作为文件变更校验,而Fingerprint可以作为数据的唯一标记。
在命名时,优先使用FingerprintChecksum,或其它特定指定用途的术语。当以上均不合适时,回退到更泛化的概念,即Digest。流式编程
Stream
SourceSink
PipePiped
流式编程通常有自己的专有词汇表。具体地:Stream表示流式Source表示数据源(输入),Sink表示数据汇(输出)。动词词汇表,如mapreducejoinfilteriteratedowindowkeyevictpeektriggerslide。。。
原则是:选择你的团队里最常使用的流式处理系统所使用的词汇表。状态
StateStatus
很讽刺地,很多人认为这两个词有区别,但是他们认为区别的点各不相同。见下文参考文献。笔者倾向于认为它们其实没什么本质区别。
鼓励使用State表示状态。因为HTTP和RPC已经占用了Status这个术语,为了避免误解,使用State表示自定义状态。
参考:https:stackoverflow。comquestions1162816namingconventionsstateversusstatushttps:softwareengineering。stackexchange。comquestions219351stateorstatuswhenshouldavariablenamecontainthewordstateandw鼓励使用State:https:google。aip。dev216计数
NumCountSizeLengthCapacityNumCount表示数量,但不强制是某个collection的长度。推荐使用Count。SizeLength表示容器1的当前容量。遵循语言惯例,通常使用Size。Capacity通常表示容器的最大容量。方法动词
动词是句子的精髓。选择精准的动词是代码可读性的关键。本章对动作做了分类,并且提供了部分备选。如果动词有反义词,它们会被聚合在一个词条中。本章的词汇有两种:动词以执行一个动作为主的某些行为类,即er模式,如Producer。able模式,如Writable是类似的,因为不再赘述。创建提供
ProducerProviderSupplierGeneratorConstructorFactory
Builder。build
Verbs:createfromofwithvalueOfinstancegetInstancenewInstancegetFoonewFoogetpeekmakegenerate
创建提供名词辨析:ProducerSupplierProvider同义词,都表示提供一个对象。这些提供者可能是惰性的(Lazy)。实例未必由这些提供者创建(虽然通常是)。它们对应的动词是工厂方法的常见命名,即:createfromofwithvalueOfinstancegetInstancenewInstancegetFoonewFoo推荐在项目中使用同一种命名。推荐使用offrom,更短。Generator通常专指某些需要经过计算的、特殊的对象,例如ID。对应的动词是generate,强调全新生成。Constructor通常是指一个复杂对象的构建器,不是指构造函数。它通常用于比Builder更复杂的构建(Builder通常不会附带逻辑)。Factory是专职的工厂类。当工厂方法较为复杂,需要抽出,或者有状态的工厂时使用。对应上文工厂方法的常见命名。Builder见前文〔Builder构建者模式〕
动词辨析:getvspeekget是广义的获取,在绝大部分场景下适用peek也是获取对象,但是这里强调的是对原对象无副作用。在函数式编程中会用来作为不破坏数据流的旁路操作。createvsmakevsgenerate同义词,表创建。推荐在项目中保持一致。消费
Consumer。acceptconsumepoll
消费名词:Consumer是最常见的消费者,通常表示某个数据流的终端消费方。对应的动词是accept或是consume,遵守所使用消息队列框架的命名风格,否则,项目内保持一致。poll特指数据是通过轮询(poll),即Consumer通常主动获取消息,而非被推送(push)后处理。
注意区分轮xun中文的歧义:poll翻译为轮询,指一个客户端间断性地向外进行获取数据的行为策略。roundrobin翻译为轮循,指以单一的方向循环接受信息资源的分发策略。
注意轮询是poll不是pull,虽然后者直觉上是拉取,但poll强制间断性地主动地采样获取数据,是正式的计算机术语。查找
Verbs:
findsearchquery
同义词。推荐在项目中保持一致。具体地,这几个词实际上有细微的不一致。通常情况下它们可能有以下区分:find查询单个结果,search查询一列符合条件的结果find表示找到,即终态,search表搜索,即行为。query表示查询,类似于search,但是暗示可能会有更高的成本。
但是,不要做这种程度的细分,大部分人认为它们是同义词。
参考https:stackoverflow。comquestions480811semanticdifferencebetweenfindandsearch拷贝
Verbs:
copyclone
同义词。遵循语言惯例。
Java使用clone。GoC使用copy。添加
Verbs:
addappendputinsertpush
动词辨析:append专指添加到列表末。insert强调可以插入到列表的任何位置。add是通用的列表添加方案,add(E)等同于append,add(index,E)等同于insert。addAll用于批量添加。put通常用于列表之外的添加场景。如map,iostream。push仅用于某些数据结构,如栈。
对于自定义的可添加api,应该贴近底层的标准库的数据结构所使用的动词。作为泛用的添加,使用add。更新
Verbs:
setupdateedit
同义词。在代码API中使用set,在RPCAPI中使用update。删除
Verbs:
removedeleteeraseclearpop
动词辨析:removedeleteerase是同义词。严格来说,remove指移除,即暂时从容器中取出放置在一边,deleteerase指删除,即将对象整个清除。但是在日常编程中不需要做这种区分。通常,代码API中使用remove(或依语言惯例),RPCAPI中使用delete作为标准方法。clear通常表示1)清理列表,等效于removeAll2)清理状态,即恢复类到初始化状态。pop只在栈队列等数据结构中使用,通常会返回对象并从数据结构中移除。编排
SchedulerDispatcherCoordinatorOrchestratorDelegator
Verb:scheduledispatchorchestrate
SchedulerDispatcher均借用于操作系统概念。
名词辨析:Scheduler:通常Scheduler用于分发中长期Job。换言之,Scheduler通常涉及到资源分配。对应动词为scheduleDispatcher:通常只负责接受事件,采用某些固定的策略分发任务,例如roundrobin。不涉及资源分配。对应动词为dispatchCoordinator:通常作为SchedulerDispatcher的同义词。鉴于其模糊性,推荐使用更细化的SchedulerDispatcher对应动词为coordinateOrchstrator:执行比简单的分发,即schedulerdispatcher更复杂的任务流程编排。通常,这些任务有前后依赖关系,会形成一个有向无环图。对应动词为orchestrate。Orchestrator的输出通常是工作流,即Workflow。Delegator:专指委任。虽然形式类似,但是Delegator强调单纯的委任。参见〔Delegation:委派模式〕。对应动词为delegate,但通常不会使用。检查验证
ValidatorCheckerVerifier
Verb:validatecheckverifyassert
ValidationVerification的明确区分来自于软件测试。Validation通常指对产品符合用户顾客预期的验证。外部用户会参与。Verification通常指产品合规符合给定规范。通常是内部流程。
在程序中,不沿用这种区分。通常:Validator用于输入检测Verifier用于运行的不变量检测
具体地:check用于输入校验。validate用于复杂的输入校验。assertverify用于不变量验证,尤其在单元测试中publicvoidprocess(Strings,ComplicatedObjectco){checkNotNull(s);checkvalidateComplicatedObject(co);validate}TestpublicvoidtestProcess(){process(ss,co);Truth。assertThat(。。。);assertverifyZeroInvocations(co);verify}执行操作
TaskJobRunnable
ExecutorOperatorProcessorRunner
Verb:execexecutedoprocessrun
名词辨析:Runnable是泛用的带上文的可执行代码块。Task粒度比Job更细Job通常是耗时更长的任务
但是,推荐不做区分,认为它们都是同义词。使用Task或者Job作为类名。
名词辨析:ProcessorExecutorOperator是从计算机架构借用的概念。Executor:常见。通常对应JobTask对应execute。exec是可接受的公认的缩写。Operator:通常对应某些具体的操作类。更多使用本义,即操作符。对应do。Processor:更多在文本文档(workdocumentprocessor)、数据处理(dataprocessor)语境下使用。对应process。Runner:通常对应Runnable对应run
但是,推荐不做区分,认为它们都是同义词。日常编程中,使用Executor作为Job执行器。开启vs关闭
toggleswitchenabledisableturnOnturnOffactivatedeactivate
二元状态的开启关闭。上述全是同义词。
在项目中保持统一。注意比起toggle(bool)和switch(bool),更推荐分离的enabledisable。读取vs写入
ReaderPrefetcherFetcherDownloaderLoader
Verb:readgetfetchloadretrieve
WriterUploader
Verb:writeupload
Lifecycle:
openclose
名词辨析:Reader通常是从stdio文件其它Source中读取。对应动词readFetcher通常是从远端拉取数据对应动词fetchDownloader类似于Fetcher,但是通常内容是文件等blob,而非结构化数据对应动词downloadPrefetcher强调预热拉取,通常是拉取到缓存中。对应动词prefetch或是简单的fetchLoader是泛用词汇,表示广义的加载。通常可以表示上述的任何一种。对应动词loadRetrieve是Fetch的同义词。具体地,fetchload是有语义的细微差别。但是,不需要做具体的细分。
优先使用readfetchdownload,当均不合适时,回退到load。序列化vs反序列化
Serializer
Verb:serializepackmarshal
Deserializer
Verb:deserializeunpackunmarshal
动词辨析:pack指打包,将数据打包为一个不可拆分的(通常是不透明的)对象serialize指序列化,将数据转换为可以被存储传输的(通常是二进制)格式。marshal强调意图将一个对象从程序A转移到程序B中。
但是,不需要做这个区分。可以认为它们都是同义词。按语言惯例使用:C:SerializeJava:SerializeGo:MarshalPython:Pack
注意反序列化是deserialize,比unserialize更常见。但packunpack,marshalunmarshal。https:en。wikipedia。orgwikiMarshalling(computerscience)https:en。wikipedia。orgwikiSerialization转换
ApplierConverterTransformerMapper
Verb:applyconverttransformmaptotranslate
可以认为它们都是同义词。在项目中应该保持一致。严格来说,Mapper更多指同一数据的两种形式的双向映射,例如数据库存储和运行时对象。在ApplierConverterTransformer中,Applier最为常见,因为源自设计模式。Mapper在框架中较常见。匹配
FilterMatcher
Verb:queryfiltermatch
可以认为它们都是同义词。在项目中应该保持一致。事件
Event
ListenerNotifierVerbs:notify
ObserverObservableVerbs:observe
HandlerVerbs:handle
PublisherSubscriber
PublisherConsumer
在〔ObserverPattner:观察者模式〕中已经解释。Observer是正宗的观察者ListnerNotifier通常可以用来作为ObserverObservable的同义词。但是Listener也可能表示其它含义,如TraceListener,视框架而定。Handler也是同义词。它与ListenerObserver的区别在于,它表示唯一的事件处理器。而ListenerObserver可能有多个。PublisherSubscriber:在事件流系统中,表示1:N广播订阅。ProducerConsumer:在整个流系统中,专指1:1生产消费。
见https:stackoverflow。comquestions42471870publishsubscribevsproducerconsumer文本处理
RegexPatternTemplate
PrunerStripperTrimmer
FormatterPrettier
ResolverParserExpander
Verb:compileparseresolveexpand
Verb:formatsplitseparatemergejoin
通常,一个程序中有20的代码在处理字符串。所以与文本相关的内容非常多。这里没有列出全部。
模板名词解析:Regex专指正则表达式。另一个常见的缩写是Regexp。应该与语言保持一致。C使用Regex。Go使用Regexp。编译使用compile。正则本身就是一种formallanguage,因此使用compile是正统。匹配对应动词为expandmatchmatchPattern在Java中表示正则表达式。虽然Pattern可能通指模式,但是通常不在编程中使用。编译使用compile对应动词为matchsplitTemplate指模板,通常不是正则形式的,而是简单的匹配替换模板,如HTML模板。对应动词为expand
修剪动名词解析:Pruner。prune:指清理掉过时的、不应存在的内容Stripper。strip:指清理掉多余的、过度生长的内容Trimmer。trim:泛指修剪,使其更好看。
但是,PruneStripTrim在编程中通常认为是同义词。它们通常情况下:StripTrim指去掉头尾的多余空格Prune可能会进行其它的裁剪
语言可能会为之赋予特殊含义,例如在Java11中,Trim会清理掉所有的普通空格,而Strip会清理掉所有的Unicode空格。https:stackoverflow。comquestions51266582differencebetweenstringtrimandstripmethodsinjava11
格式化动名词解析:Formatter。format是将对象进行格式化。通用名词。Prettier。pprint专指将数据整理为便于人类可读的的输出格式。典型的例子是Python的pprint。
解析动名词解析:Expander。expand通常用于DSLSchema解析,专指将某些DSL展开,如变量替换,展开glob。Parser。parse类似于parse,但强调将文本进行句法解析,形成格式化的中间层表示。借用了编译器术语。Resolver。resolveResolve通常指从人类可读的定义(可能有歧义或不精确)向机器可读的定义(精确的、可解析的)的转换。例如,域名ip的解析,依赖包的版本号的解析(打平)。(!)resolve不同于expandparse的文本解析。这是一个相同中文不同英文的易混淆例子。生命周期
Lifecycle
InitializerFinalizer
Verb:
initsetupprepare
pauseresume
startbegin
endterminatestophalt
destroyreleaseshutdownteardown
生命周期解析:一个对象的生命周期,称为Lifecycle,通常有以下流程:创建。通常由语言特性支持,不属于生命周期管理范围。初始化:init。init是initialize的全称,通常用来初始化一个类到可用状态。应该尽量避免创建之外的额外初始化步骤,一个对象应该尽可能在创建后就处于已初始化状态。额外的状态会让这个类更难正确使用。setupprepare是init的同义词。应该在项目内统一,推荐为init。setUp通常在测试中使用,用于作为每个测试用例设计的前置步骤。initvsprepare:具体地细分,init的语义通常指在类生命周期层面处在正常可执行状态,prepare的语义通常指在业务层面做好准备开始:startbegin。通常用于这个对象正式开始正常工作,即切换到running状态。在切换到其它状态之前这个类会一直保持在running状态。startbegin是同义词。通常使用start作为动词开始,使用begin作为列表的头。暂停:pause。pause应该使得类暂停运行,从running状态切换到paused状态。这段时间这个类应该不再工作。恢复:resume。resume与pause是成对的。resume会恢复paused到running状态。通常,pauseresume可以无限次随时切换。停止:stopendterminatehalt。停止类运行,与start对应。通常情况下,一个类stop意味着不会再重新启动。通常情况下,停止状态的类应该拒绝任何请求。stopendterminatehalt是同义词。不要做区分处理,在项目中保持一致。销毁:destroyreleaseshutdownteardownexit。彻底销毁对象。此后,对象不再处于可用状态。destroyreleaseshutdownteardown是近义词。具体地:destroy强调销毁对象。release强调释放资源。teardown通常与setup对应。exit通常指程序退出。shutdown是通用彻底关闭的动词。当destroyrelease不合适时,回退到shutdown。使用gracefullyShutdown表示优雅关闭。这通常意味着是前几个行为的集合:停止服务、释放资源、刷新缓冲区、销毁对象。计算
Calculator
Verb:
computecalculatecalc
使用Calculator而非Computer表示某个运算的执行器。Computer虽然也是计算器,但是在代码语境下有歧义。
computecalculatecalc可以认为是同义词。如果是Calculator,使用calculate。其它情况下,使用compute。元数据(配置环境。。。)
OptionConfigConfigurationSettingPreferencePropertyParameterArgument
ContextEnvironment
InfoMetadataManifestVersion
配置名词解析:这个有类似的名词辨析,但是它们在编程时通常认为都是配置的同义词。它们还会出现在用户界面,尤其是SettingsOptionsPreferences。
在编程的角度,OptionConfigConfiguration是同义词,均表示配置。惯例使用Options作为子类定义一个类所需的配置,尤其是作为依赖注入时。
使用Property表示单个属性,而且通常是kv结构。换言之,OptionConfig通常由多个Properties组织。只有当Property是动态属性时,才定义特殊的Property类,否则,在Option中定义具体的域表示Property。structOptions{intfurlayercount;Goodintfurlayercountproperty;Bad!PropertyunnecessarystructColorProperty{inta;intr;intg;intb;}Bad!PreferColor。ColorPropertycolor;}
参数解析:Parameter:通常表示在接口处定义的参数Argument:指实际传入接口的参数
例如:funcfoo(paramstring)foo(arg)
https:stackoverflow。comquestions156767whatsthedifferencebetweenanargumentandaparameter
上下文名词辨析:Context指上下文,通常用于在API之间传递与一次执行相关的信息。在RPC处理中非常常见,例如https:pkg。go。devcontext。Environment指环境。这个名词从系统环境变量而来。通常,这表示在程序启动后保持稳定的环境数据,不随所执行的内容(如rpc请求)变化而变化。
元数据辨析:Info泛指信息。而元数据相当于特定的关于数据的信息。Metadata标准用语,专指元数据。避免使用Info代表元数据。Manifest专指文件清单,描述一个模块中的文件功能其它组成结构的列表。Manifest来自于货运术语,ShipManifest用以列出所有的船员和船队所有的船只。Version专指程序的版本元数据,例如TrpcVersion。如果一个类专指版本,使用Version是最精确合适的。
盘点那些消失的网红半藏森林最令人惋惜,李子柒最为神秘?流量时代,网红是乱花渐欲迷人眼,不同类型的层出不穷,然而在滚滚洪流之中,有些人迅速崛起又快速落幕,他们中很多是自作自受,但最后一位却让人意想不到十分惋惜。半藏森林被称为纯欲鼻祖,半
NBA2023年全明星阵容解析詹杜库首发无忧,嘴哥登哥全部落选全文约3400字,欢迎收藏后阅读,感谢点赞!NBA2023年全明星大赛将于明年2月20日在美国盐湖城举办,而近日全明星的球迷投票活动已经拉开帷幕。这里简单说下投票规则,每位球迷都可
12月21日大盘3连阴,大消费局部战役,要反弹了吗?可以补仓?今日大盘缩量三连阴,市场赚钱效应较差,全A下跌3569家,跌停18家,上涨1296家,涨停34家,平盘2150家,停牌9家。两市成交5747亿,其中沪市下跌0。17,成交2269亿
注意这几点,非常重要!面对当前疫情防控的新形势正确认识疫情防控工作每个人都是一道防线做好自己健康的第一责任人重点在于防患于未然保护自己和家人这几点很重要没有旁观者没有局外人每个人都是自己健康的直接责任人
俄军在乌克兰战场的表现,给解放军五大启示!普京已意识到不足12月21日,普京主持召开了俄罗斯国防部会议。原本外界猜测的,普京会宣布重大消息并没有出现。而是对俄罗斯的军队情况,进行了总结,并对未来发展提出了规划。俄防长绍伊古的讲话,也显示出
阳康之后循序渐进回归运动新冠康复后,许多运动爱好者迫不及待地回到健身房,也有不少人意识到日常锻炼的重要性,急于通过运动唤回活力满满的状态。对此,国家体育总局运动医学研究所体育医院老年康复科(中医康复)主任
大鱼奇境!武汉欢乐谷灯光节要来啦武汉欢乐谷灯光节即将启幕。长江日报大武汉客户端12月18日讯(记者晋晓慧通讯员韩璇)三大光影奇境35万方幻梦灯海多重奇幻演艺长江日报记者了解到,12月24日,华中首个大鱼奇境灯光节
迪丽热巴认真过冬,穿泰迪熊大衣戴双层口罩,筷子腿好惹眼头条创作挑战赛寒冷的冬天来了,每个人都在瑟瑟发抖。尤其是需要外出的朋友,更是提心吊胆。穿好棉衣戴好口罩,成为最贴心的叮嘱。不管是明星还是素人,都一定要做好防护,认真过冬。迪丽热巴真
以三力汇聚动能,跑出产改加速度原标题以三力汇聚动能,跑出产改加速度(主题)厦门市推进产业工人队伍建设改革工作纪实(副题)从一线研究人员到劳模创新工作室带头人,再到全国五一劳动奖章获得者,三达膜环境技术股份有限公
徐小明周一操作策略前天说大盘上涨了25天,今天更正一下,这波上涨从低点到高点,一共是26天,12月6号那天应该算在上涨周期里。26这数字是够大了,前面也说了1浪26天2浪27天3浪30天4浪不会特别
ARM断供,X86不授权,RISCV才是中国芯崛起的希望?不管大家承认不承认,ARMX86架构才是当前芯片架构中的王者,其它众多架构,也许曾经辉煌过,但目前根本无法与ARMX86抗衡。但大家都清楚,X86基本不对外授权,就算目前有企业拿到