首页 文章 精选 留言 我的

精选列表

搜索[卡顿],共9801篇文章
优秀的个人博客,低调大师

android中的TabHost(选项)的使用

效果图如下: 其中main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <!-- 定义第一个标签页的内容 --> <LinearLayout android:id="@+id/tab01" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="女儿国国王 - 2010/12/12" android:textSize="11pt" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="东海龙女 - 2010/12/18" android:textSize="11pt" /> </LinearLayout> <!-- 定义第二个标签页的内容 --> <LinearLayout android:id="@+id/tab02" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="白骨精 - 2010/08/12" android:textSize="11pt" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="蜘蛛精 - 2010/09/20" android:textSize="11pt" /> </LinearLayout> <!-- 定义第三个标签页的内容 --> <LinearLayout android:id="@+id/tab03" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:textSize="11pt" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="孙悟空 - 2010/09/19" android:textSize="11pt" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="猪八戒 - 2010/10/12" android:textSize="11pt" /> </LinearLayout> </TabHost> AndroidManifest.xml的代码如下: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.demo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MyTab" > <intent-filter> <action android:name="android.intent.action.MAIN" > </action> <category android:name="android.intent.category.LAUNCHER" > </category> </intent-filter> </activity> </application> </manifest> 其中MyTab.java代码如下: package android.demo; import android.app.TabActivity; import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.widget.TabHost; import android.widget.TabHost.OnTabChangeListener; @SuppressWarnings("deprecation") public class MyTab extends TabActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TabHost tabHost = getTabHost(); // 设置使用TabHost布局 LayoutInflater.from(this).inflate(R.layout.main, tabHost.getTabContentView(), true); // 添加第一个标签页 tabHost.addTab(tabHost.newTabSpec("tab1").setIndicator("已接电话") .setContent(R.id.tab01)); // 添加第二个标签页 tabHost.addTab(tabHost .newTabSpec("tab2") // 在标签标题上放置图标 .setIndicator("呼出电话", getResources().getDrawable(R.drawable.ic_launcher)) .setContent(R.id.tab02)); // 添加第三个标签页 tabHost.addTab(tabHost.newTabSpec("tab3").setIndicator("未接电话") .setContent(R.id.tab03)); } } ============================================================================== 本文转自被遗忘的博客园博客,原文链接:http://www.cnblogs.com/rollenholt/archive/2012/05/17/2506624.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

经过一骚操作,项目编译打包速度提升一倍!

背景 我们的公司的系统中是以微服务方式开发的。每个项目就是一个微服务,有非常多的微服务,比如用户服务、后台管理服务、订单服务、物流服务、商品服务等等。 其中每个项目都使用的 gradle 构建工具打包、发布、部署。 gradle 是一个项目构建工具,与 maven 类似,它提供非常强大的包依赖管理功能。 现象与问题 有些项目打包编译速度非常慢,有时需要 5 到 10 分钟,而且还经常失败。 有时候点击三方包的查看源码,通过 IDE 进入类的 class 文件,点击下载源码,控制台显示失败。 项目发布的 api 包中,除了 rpc 接口必须的 model,还增加了很多无用的包,导致其他项目引入后,jar 过多。 分析 带着这三个问题开始我们的分析。 问题一:项目打包编译速度非常慢,并且经常失败 通过查看打包日志,发现失败的信息是,编译某个模块时时间过长、通过 maven 仓库下载包的时候经常失败、显示超时信息等等,最终输出不能解析 xxx.xxx.xxx 包。 然后再次重新打包,又经过一段时间漫长的等待,可能会成功,但是时间很长。 在打包时增加 debug 选项查看下打包过程中出现错误的日志信息。 我们发现,项目模块在编译打包的时候,出现访问远程仓库资源 403 的信息。 00:29:59.125 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Using com.xgoods:xabtest-proto:v0.3.0 from Maven repository 'nexus' 00:29:59.125 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.DynamicVersionResolver] Discarding resolve failure. org.gradle.internal.resolve.ModuleVersionResolveException: Failed to list versions for com.xgoods:xabtest-proto. at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingModuleComponentRepository$ErrorHandlingModuleComponentRepositoryAccess.listModuleVersions(ErrorHandlingModuleComponentRepository.java:126) ...省略... at java.base/java.lang.Thread.run(Thread.java:834) Caused by: org.gradle.api.resources.ResourceException: Unable to load Maven meta-data from http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml. at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.load(MavenMetadataLoader.java:55) at org.gradle.api.internal.artifacts.repositories.maven.MavenVersionLister.listVersions(MavenVersionLister.java:48) at org.gradle.api.internal.artifacts.repositories.metadata.DefaultMavenPomMetadataSource.listModuleVersions(DefaultMavenPomMetadataSource.java:73) at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver.doListModuleVersions(ExternalResourceResolver.java:210) at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver.access$200(ExternalResourceResolver.java:90) at org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver$RemoteRepositoryAccess.listModuleVersions(ExternalResourceResolver.java:440) at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.CachingModuleComponentRepository$ResolveAndCacheRepositoryAccess.listModuleVersions(CachingModuleComponentRepository.java:360) at org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ErrorHandlingModuleComponentRepository$ErrorHandlingModuleComponentRepositoryAccess.listModuleVersions(ErrorHandlingModuleComponentRepository.java:121) ... 107 more Caused by: org.gradle.api.resources.ResourceException: Could not get resource 'http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml'. at org.gradle.internal.resource.ResourceExceptions.failure(ResourceExceptions.java:74) at org.gradle.internal.resource.ResourceExceptions.getFailed(ResourceExceptions.java:57) at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.copyToCache(DefaultCacheAwareExternalResourceAccessor.java:201) at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.access$300(DefaultCacheAwareExternalResourceAccessor.java:54) at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor$1.create(DefaultCacheAwareExternalResourceAccessor.java:89) at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor$1.create(DefaultCacheAwareExternalResourceAccessor.java:81) at org.gradle.cache.internal.ProducerGuard$AdaptiveProducerGuard.guardByKey(ProducerGuard.java:97) at org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor.getResource(DefaultCacheAwareExternalResourceAccessor.java:81) at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.parseMavenMetadataInfo(MavenMetadataLoader.java:61) at org.gradle.api.internal.artifacts.repositories.maven.MavenMetadataLoader.load(MavenMetadataLoader.java:51) ... 114 more Caused by: org.gradle.internal.resource.transport.http.HttpErrorStatusCodeException: Could not GET 'http://repo.spring.io/release/com/xgoods/xabtest-proto/maven-metadata.xml'. Received status code 403 from server: Forbidden at org.gradle.internal.resource.transport.http.HttpClientHelper.processResponse(HttpClientHelper.java:158) at org.gradle.internal.resource.transport.http.HttpClientHelper.performGet(HttpClientHelper.java:84) at org.gradle.internal.resource.transport.http.HttpResourceAccessor.openResource(HttpResourceAccessor.java:43) at org.gradle.internal.resource.transport.http.HttpResourceAccessor.openResource(HttpResourceAccessor.java:29) at org.gradle.internal.resource.transfer.DefaultExternalResourceConnector.openResource(DefaultExternalResourceConnector.java:56) at org.gradle.internal.resource.transfer.ProgressLoggingExternalResourceAccessor.openResource(ProgressLoggingExternalResourceAccessor.java:37) at org.gradle.internal.resource.transfer.AccessorBackedExternalResource.withContentIfPresent(AccessorBackedExternalResource.java:130) at org.gradle.internal.resource.BuildOperationFiringExternalResourceDecorator$11.call(BuildOperationFiringExternalResourceDecorator.java:237) 我们发现,项目模块在编译打包的时候,一些 jar 原本是只是会存在我们公司私服上的的,但是却跑到了其他的远程仓库上下载,然后就出现访问远程仓库资源 403 的信息,因为 jar 只有在私服上,所以远程仓库肯定是没有的! 上述情况在打包编译的时候经常发生,原因就是一些 jar 包属于公司私服上的,下载 jar 包时,应该在私服下载,但是却一直在远程仓库下载,导致多次选择不同仓库重试下载,一定时间之后出现了超时、失败信息。 现在来查看项目的 build.gradle 文件结构。 // 公司的公用 gradle 插件 apply from: "http://xxx.xxx.look.gradle" buildscript { apply from: "http://xxx.xxx.look.gradle" repositories { mavenLocal() mavenCentral() maven { url "http://repo.spring.io/release" } maven { url 'https://repo.spring.io/libs-milestone' } maven { url "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/snapshot" } } // ...一些配置... } // project setting allprojects { apply plugin: "idea" apply plugin: "io.spring.dependency-management" apply plugin: "org.springframework.boot" repositories { maven { name "atlassian-public" url "https://m2proxy.atlassian.com/repository/public" } mavenLocal() mavenCentral() maven { url "http://repo.spring.io/release" } maven { url 'https://repo.spring.io/libs-milestone' } maven { url "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/snapshot" } maven { url 'http://maven.youzanyun.com/repository/maven-releases/' } } // 依赖管理器 dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } // ...各种配置... dependencies { compileOnly 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10' // ...依赖超级多的 jar 包,包括公司的包和三方包.... compile "xxx.xxx.xxx" ... } } // 模块一 project(':xeshop-common') { dependencies { // 依赖一些模块 和 一堆 jar 包 ... } } // 模块二 project(':xeshop-dao') { dependencies { // 依赖模块一 compile project(':xeshop-common') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块三 project(':xeshop-service') { dependencies { // 依赖模块二 compile project(':xeshop-dao') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块四 project(':xeshop-thirdbiz') { dependencies { // 依赖模块三 compile project(':xeshop-service') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块五 project(':xeshop-rpc') { dependencies { // 依赖模块三 compile project(':xeshop-service') // 依赖模块四 compile project(':xeshop-thirdbiz') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块六 project(':xeshop-api') { dependencies { // 依赖模块三 compile project(':xeshop-service') // 依赖模块四 compile project(':xeshop-thirdbiz') // 依赖模块五 compile project(':xeshop-rpc') // 依赖一些模块 和 一堆 jar 包 ... } ...其他配置... } ...等等模块... 通过查看上面的 build.gradle 配置,会发现一些问题: allprojects 配置中配置了很多的依赖包,并且采用了 io.spring.dependency-management、org.springframework.boot 这两个插件,来管理依赖,并且设置了依赖管理器 dependencyManagement 配置,这就导致了要管理所有模块的依赖包。 我们去掉 allprojects 中的 io.spring.dependency-management、org.springframework.boot插件,以及 dependencyManagement 配置。修改某些需要被依赖的 jar 包指定固定版本号。 allprojects 配置了仓库地址,但是 buildscript 已经配置了相关的仓库地址; 我们去掉 allprojects 中的 repositories 配置。 问题二:下载三方源码包失败 下载源码的操作如下。 异常信息是: Caused by: org.gradle.internal.resolve.ModuleVersionNotFoundException: Could not find org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2. Searched in the following locations: - file:/xxx.xxxx/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Greenwich.SR2/spring-cloud-dependencies-Greenwich.SR2.pom Required by: project :xeshop-common 这个异常信息是说,在 /xxx.xxxx/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Greenwich.SR2/spring-cloud-dependencies-Greenwich.SR2.pom 这个地址上找不到 org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2 包,需要 xeshop-common 项目。 我们查看 build.gradle 文件,发现allprojects 配置中,配置依赖管理类,如下配置: dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:Greenwich.SR2" } } mavenBom 是为了防止用Maven管理Spring项目时,不同的项目依赖了不同版本的Spring,可以使用Maven BOM来解决者一问题。 但是在 allprojects 配置中配置,会导致所有的项目的源码都要经过它来管理下载。 我们把它注释掉。 问题三、各个模块之间的依赖有些重复 将 allprojects 中,除了一些必须全部模块公用的jar,其他的 jar 全部移除,并且放入 xeshop-common 依赖中。 解决 通过上面的分析,我们修改后的 build.gradle 如下配置: // 公用 gradle 插件 apply from: "http://xxx.xxx.look.gradle" buildscript { apply from: "http://xxx.xxx.look.gradle" repositories { mavenLocal() mavenCentral() maven { url "http://repo.spring.io/release" } maven { url 'https://repo.spring.io/libs-milestone' } maven { url "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/snapshot" } } // ...一些配置... } // project setting allprojects { apply plugin: "idea" // ...各种配置... dependencies { // 这里只留一些必要的公共包,其他的包全部移动到 xeshop-common 模块中 ... } } // 模块一 project(':xeshop-common') { dependencies { // 添加依赖包 ... } } // 模块二 project(':xeshop-dao') { dependencies { // 依赖模块一 compile project(':xeshop-common') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块三 project(':xeshop-service') { dependencies { // 依赖模块二 compile project(':xeshop-dao') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块四 project(':xeshop-thirdbiz') { dependencies { // 依赖模块三 compile project(':xeshop-service') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块五 project(':xeshop-rpc') { dependencies { // 依赖模块四 compile project(':xeshop-thirdbiz') // 依赖一些模块 和 一堆 jar 包 ... } } // 模块六 project(':xeshop-api') { dependencies { // 依赖模块五 compile project(':xeshop-rpc') // 依赖一些模块 和 一堆 jar 包 ... } ...其他配置... } ...等等模块... 对项目重新进行编译打包,会发现速度非常快,丝滑流畅~ 总结 通过优化上述配置,更加深刻的理解了 gradle 配置,以及它的依赖管理; 遇到类似的问题,不要慌,在执行 gradle 打包命编译令时增加 debug、stack 参数,可以输出更加多有用的信息,帮助我们排查问题; 项目中的依赖很多的很复杂的时候,需要花时间梳理配置信息,删繁就简,整理出一个大体框架,更好的帮助我们理解与分析。

优秀的个人博客,低调大师

这一神操作!我把 3000 行代码重构成 15 行!

如果你认为这是一个标题党,那么我真诚的恳请你耐心的把文章的第一部分读完,然后再下结论。如果你认为能够戳中您的G点,那么请随手点个赞。 把三千行代码重构为15行 那年我刚毕业,进了现在这个公司。公司是搞数据中心环境监控的,里面充斥着嵌入式、精密空调、总线、RFID的概念,我一个都不懂。还好,公司之前用Delphi写的老客户端因为太慢,然后就搞了个Webform的替代,恰好我对Asp.Net还算了解,我对业务的不了解并不妨碍我称成为这个公司的一个程序员。 小公司也有小公司的好,人少,进去很快负责代码开发。我当然也就搞这个数据中心智能管理系统啦。 这个系统非常的庞大,尤其牛逼的是支持客户端组态,然后动态生成网页,数据还能通过Socket实时监控(那时我还真就不懂网络编程)。这个对于当时的我来说,真真是高、大、上呐!!当时跟着了解整个系统大半个月才算能够调试,写一些简单的页面。 在维护系统的过程中,时不时要扩展一些功能,也就接触了下面这个类: 看到没有,就是当年最最流行的三层架构的产物,对于刚出茅庐的毛头小子来说,这是多么专业的文件头注释,还有反射也就算了,这构造函数还能静态的,还能私有的?那时刚接触这么高大上的代码的我,瞬间给跪了! 但是,类写多了,我就感觉越来越别扭,就是下面这段代码: 每增加一个表,除了要改接口、要改DAL、要改BLL之外,还得在这个工厂类添加一个方法,真真是累到手抽筋,即使有当时公司了的G工给我推荐的神器——动软代码生成器,这粘贴复制的几遍,也是让我感觉到异常繁琐,有时候打键盘稍微累了点,还把复制出来代码改错了,你妹的,难道这就是程序员该干的事情,不,绝对不是!我想起了一句至理名言:当你觉得代码重复出现在程序中的时候,就应该重构了。是的,在这句话的指导下,我开始了折腾,决定挑战这个高大上的代码,事实证明,思想的力量是无穷的。 那么,怎么修改呢,仔细观察之后,发现其中className的生成跟返回的类型非常类似,只是一个是类名,一个是字符串,这两者之间应该能够关联起来。于是google了一下(当时GFW还没猖獗起来哈),隐隐约约就找到了“反射”这两个字,深入了解之后,确定可以完成。 接下来,就是返回的类型了,返回的类型并不固定,但是似乎很有规律……这个似乎好像在哪里见过,对了,模板,C++课程上有讲过的,于是再次google,了解到了C#中使用了泛型代替了C++中的模板。在学习完泛型和反射之后,并参考了网上的一些文章,我捣鼓出了下面的代码: 没错,就是它了,三层架构年代最流行的工厂类…… 看着原来滚十几屏幕的代码,变成了十多行的代码,真是爽到了骨子里去了,太干净了!唯一让我担忧的是,我进公司的时候,帮忙整理公司申请软件著作权都是需要代码量的,根据代码多少行来评估软件的大小,万一老板知道了我非但没有帮公司增加代码量,还减少了,会不会立即把我开掉?我没敢给我们老板展示我优秀的成果。 所幸,这段代码非但没有出过任何问题,还避免了以前同事老是在新增一个类之后,把代码复制过来,但是没有正确修改的问题,大大提高了效率。虽然,我没敢大事宣布我的劳动成果,但是这次成功的修改,则彻底让我走上了代码重构的不归路。 看到这里,大家应该知道这个案例是否真实的了吧。我相信,从08年开始的码农们,看到这种类似的代码绝对不比我少。那么,我想告诉你们的是什么呢? 要在编程过程中多思考 编程的思想很重要,请多看点经典的书 从小处着眼,慢慢重构,尤其在应对一个大型的系统 当重复出现的时候,你应该考虑重构了 粘贴复制的代码越少,你的系统越稳定 少用代码生成器 我们来分析一下,为什么我之前的前辈会写出上面的代码。我归结起来有以下几点: 因为使用了动软代码生成器,生成代码方便,就没多想了。 三层架构的概念倒是了解了,但是没有去深入思考就拿来应用 遇到重复的代码,没有重构的概念,这是思想的问题——思想比你的能力重要 至今为止,还是很多人使用代码生成器,那么我们应该怎么对待这个问题呢。我认为,代码生成器确实可以减少你不少工作,但是少用,那些重复性的工作,除了部分确实是没有办法的,其他大部分都是可以通过框架解决的,举例来说,像三层架构,真正需要用到代码生成器的,也就是Model类而已,其他的完全可以在框架中完成。 因此你要竭尽全力的思考怎么在框架中来减少你的重复性工作,而不是依赖于代码生成器。 另外,如果你还是在用相关的代码生成工具,请重新定义“动软代码生成器”的代码模板,自己写一个模板;或者使用CodeSmith来完全制定自己的代码生成,因为动软给的代码模板真心乱,比如下面这段代码: for (int n = 0; n < rowsCount; n++) { model = new DBAccess.Model.eventweek(); if(dt.Rows[n]["GroupNo"].ToString()!="") { model.GroupNo=int.Parse(dt.Rows[n]["GroupNo"].ToString()); } if(dt.Rows[n]["Week0"].ToString()!="") { model.Week0=int.Parse(dt.Rows[n]["Week0"].ToString()); } if(dt.Rows[n]["Week1"].ToString()!="") { model.Week1=int.Parse(dt.Rows[n]["Week1"].ToString()); } } 首先,你就不能用 var row=dt.Rows[n] 替代吗?其次,直接用int.Parse如果抛出了异常性能得有多低?再次,这段代码要是有点修改,我不是要每个dt.Rows[n]得改一遍? 不要重复发明轮子 我们再来看看其他的一些代码: public List<string> GetDevices(string dev){ List<string> devs=new List<string>(); int start=0; for(int i=0;i<dev.Length;i++){ if(dev[i]=='^'){ devs.Add(dev.SubString(start,i)); start=i+1; } } return devs; } 有没有很眼熟,没错,这就是对String.Split()函数的简单实现。我的前辈应该是从c++程序员转过来的,习惯了各种功能自己实现一遍,但是他忽略了C#的很多东西。我们不去评判这段代码的优劣,而实际上他在很长一段时间都运行得很好。我们来看看使用这一段代码有什么不好的地方: 重复发明轮子。花费了额外的时间,函数的健壮性和很差 可读性差。其实是一个很简单的功能,但是用上了这么一段函数,起初我还以为有什么特别的功能。 那么,我们应该怎样去避免重复发明轮子呢?我从个人的经历来提出以下几点,希望能够对各位有所帮助: 了解你所学的编程语言的特性。你可以看一本基础的入门书籍,把所有的特性浏览一遍,或者上MSDN,把相关的内容过一遍。 在你决定动手发明一个轮子之前,先搜索一下现成的解决方案。你还可以到CodeProject、GitHub之类的网站搜索一下。在知乎上有很多人都在批评这么一种现象,老是问一些重复性的问题,然后又职责知乎没落了,没有人回答他的问题,实际上相关问题已经有了很详细的解答,那提问之前,不能首先去搜一下是否有现成的答案,反而指责没有回答他的问题呢? 你有一定的基础之后,还应该去读一下相关的经典书籍,深入了解其中的原理。比如,你觉得你有一定的基础了,我建议你去把《CLR Via C#》多读几遍,你了解原理越多,你越是能够利用这编程语言的特性,从而来实现原本那些你认为要靠自己写代码的功能。 这里我再举一个我自己的例子。在我现有的程序中,我发现我需要越来越多的线程来执行一些简单的任务,比如在每天检测一下硬盘是否达到90%了,每天9点要控制一下空调的开启而在网上6点的时候把空调关掉。 线程使用越来越多,我越是觉得浪费,因为这些现场仅仅只需完成一次或者有限的几次,大部分时间都是没有意义的,那么怎么办呢?我决定自己写一个任务类,来完成相关的事情。说干就干,我很快把这个类写出来了。 public abstract class MissionBase : IMission { private DateTime _nextExecuteTime; protected virtual DateTime[] ExecuteTimePoints { get; private set; } protected virtual int IntervalSeconds { get; private set; } protected IEngine Engine { get; private set; } public bool IsCanceled{get{……}} public bool IsExecuting{get{……}} public bool IsTimeToExecute{get{……}} public abstract bool Enable { get; } public abstract string Name { get; } protected MissionBase(IEngine engine) { ExecuteTimePoints = null;//默认采用间隔的方式 IntervalSeconds = 60 * 60;//默认的间隔为1个小时 Engine = engine; } /// 任务的执行方法 public void Done() { if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 1) return; try { …… } finally { Interlocked.CompareExchange(ref _isExecuting, 0, 1); } } ///实际方法的执行 protected abstract void DoneReal(); } 但是,实际上这个任务方法,并不好用,要写的代码不少,而且可靠性还没有保障。当然,我可以继续完善这个类,但是我决定搜索一下是否还有其他的方法。 直到有一天,我再次阅读《CLR Via C#》,看到线程这一章,讲到了System.Threading.Timer以及ThreadPool类时,我就知道了,使用Timer类完全可以解决我的这个用尽量少的线程完成定时任务的问题。 因为从原理上来说,Timer类无论你声明了多少个,其实就只有一个线程在执行。当你到了执行时间时,这个管理线程会用ThreadPool来执行Timer中的函数,因为使用的ThreadPool,执行完成之后,线程就马上回收了,这个其实就完全实现了我所需要的功能。 等你无法重构的时候再考虑重写 我带过很多优秀的程序员,也与很多优秀的程序员共事过。有一大部分的程序员在看到一套系统不是那么满意,或者存在某些明显的问题,就总是忍不住要把整套系统按自己觉得可以优化的方向来重写,结果,重写结构往往并不令人满意。 系统中确实存在很多不合理的地方,但是有不少的这种代码,恰恰是为了解决一些特定场景下的问题的。也就是说,所有的规范以及编程的原则,其实也是有条件限制的,他可能在大部分的时候是正确的,能够指导你完成你的任务,但是,并不是在所有地方都是适用的。比如数据库范式,但实际中我们的设计往往会考虑冗余,这是违背范式的,但是为什么还有那么多人趋之若鹜呢?因为我们可能需要用空间换时间。 如果我们一开始就考虑重写,那么你可能会陷入以下的困境: 需要花更大的精力来完成一些看似简单的BUG 你要知道,有一部分看似错误或者非常不优美的代码,其实恰恰是为了解决一些非常刁钻的问题的。 再也无法兼容老的系统了 你急于把原有系统重写,却往往忽略了对原有系统的兼容,那么你新的系统的推进则会十分缓慢。而老系统的维护,又会陷入及其尴尬的情况。 过度设计,导致重写计划迟迟无法完成 有重写冲动的程序员往往是在架构设计上有一些读到的见解,他们善于利用所学的各种设计模式和架构技巧来建立系统,但是越是想尽可能的利用设计模式,越是陷入过度设计的困局,导致重写的计划迟迟都无法完成。 无法有效利用现有系统已经完成并测试的代码 如果你确实有必要进行重写,我还是建议你把代码尽可能的重构。因为重构之后的系统,能够让你更轻易的重写,又最大限度了保留以前可用的业务代码。 我举个例子,说明如何通过重构更好的利用现有代码的。 我有一个非常庞大的系统,其中有一块功能是用于数据采集、存储、告警管理以及电话、短信等告警通知。大致的结构如下: class MainEngine:IEngine{ public MainEngine(ConfigSettings config){ } public void Start(); public void Stop(); } 需要增加新的业务功能时,程序员写的代码往往是这样的:首先时修改配置类 class ConfigSettings{ public bool NewFuncEnable{get;private set;} public ConfigSettings(){ NewFuncEnable=xx;//从配置文件读取 } } 接着修改主程序: class MainEngine:IEngine{ private NewFuncClass newCls=new NewFuncClass(); public MainEngine(ConfigSettings config){ } public void Start(){ if(config.NewFuncEnable) newCls.Start(); } public void Stop(){ if(config.NewFuncEnable) newCls.Stop(); } } 在修改的过程中,往往是根据配置文件来判断新功能是否启用。上面代码会造成什么问题呢: 主程序代码和扩展功能耦合性太强,每增加一个功能都要修改主程序代码,这里非常非常容易出错。尤其是新的人进度开发组,很容易就忘主程序中增加了一些致命性的代码。比如上述的扩展功能,可能是在特定的项目中才会有这个扩展功能,但是,写代码的人忘记增加是否启用的配置选项了,导致所有的项目都应用了这个功能,而这个功能需要特定的表,这样就悲剧了。即使是你增加了配置,也是非常的不美观,因为在通用的版本中使用了这个配置,往往会让定制项目以外的人员感到困惑。 增加扩展功能的人还需对整个MainEngine代码有一定的熟悉,否则,他根本就不知道在Start方法和Stop方法进行newClas的对应方法的调用 如果你打算对这段代码进行重写,那么,你会感到非常的困难,因为你分不清楚newCls这个新实例的作用,要么你花大精力去把所有代码理清楚,要么直接就把这段新增的业务代码去掉了。 那么我们如何对这段代码进行重构呢。首先,我们把新功能注册的代码抽取出来,通过反射来实现新的功能的注册。 private void RegisterTaskHandlerBundles() { var bundles = xxx.BLL.Caches.ServiceBundleCache.Instance.GetBundles("TaskHandlerBundle"); if (bundles != null && bundles.Count > 0) { var asmCache = new Dictionary<string, Assembly>(); foreach (var bundle in bundles) { try { if (!asmCache.ContainsKey(bundle.Category)) asmCache.Add(bundle.Category, Assembly.Load(bundle.AssemblyName)); var handler = (ITaskHandler)asmCache[bundle.Category].CreateInstance(bundle.ClassName, false, BindingFlags.Default, null, new object[] { this, bundle }, null, null); _taskHandlerBundles.Add(bundle, handler); } catch (Exception e) { NLogHelper.Instance.Error("加载bundle[Name:{0},Assembly:{1}:Class:{2}]异常:{3}", bundle.Name, bundle.AssemblyName, bundle.ClassName, e.Message); } } } } 修改MainEngine代码 class MainEngine:IEngine{ private NewFuncClass newCls=new NewFuncClass(); public MainEngine(ConfigSettings config){ RegisterTaskHandlerBundles(); } public void Start(){ _taskHandlerBundles.Start(); } public void Stop(){ _taskHandlerBundles.Stop(); } } OK,现在我们再来看看怎么实现原来的新增功能:你只需按规范新建一个类,继承ITaskHandler接口,并实现接口的方法。最后在XTGL_ServiceBundle表中新增一条记录即可。我们再来看看这么做有什么好处: 新增的类只需按规范写即可,完全对MainEngine代码没有任何影响。你甚至可以把这个MainEngine代码写在一个新建的Dll中。 新增功能的这个业务类跟原来的代码解耦,非常方便进行新功能的业务测试,而无需考虑原有框架的影响 新增功能的业务类与架构完全分离,我们在重写代码中只要保证接口的稳定性,无论我们怎么把系统架构重写,我们可以马上就重用上原有的业务功能代码。 重构的目标之一,就是把框架和业务完全分离。 有志于深入了解的同学,可以了解下反射、Ioc和插件话编程等。 学会单元测试,培养你的重构意识 可能上面说了这么多,还是有很多人并不理解重构。没关系,在这里我教你们一个快速入门的办法,就是单元测试。什么是单元测试,请自行google。单元测试有什么要求? 就是要求你要把每个方法都弄成尽量可以测试的。尽量让你的方法变成是可测试的,就是培养你重构意识的利器。在你要求把方法变成可测试的过程,你就会发现你必须得不断的修改你的方法,让它的职责尽量单一,让它尽量的与上下文无关,让它尽可能通过方法参数的输入输出就能完成相关的功能,让依赖的类都尽量改为接口而不是实例。 最终,你就会发觉,这就是重构!而且是在不知不觉中,你重构的功力就会大大提升,你编程的水平也会大大提升! 看到这里,有经验的程序员就会问,你这是在鼓励我使用TDD吗?不,不是的。TDD(Test-Driven Development)鼓励的是测试驱动开发,未开发之前先编写单元测试用例代码,测试代码确定需要编写什么产品代码。 这是一种比较先进的开发方法,但是在编程的实践过程中,我认为它过于繁琐,很多中小企业很难实施,更别提我们个人开发者。我这里提倡你用单元测试培养你的重构意识,可以说是一种后驱动,用于提高你的重构能力和重构愿望,你完全可以把我的这个方法称为“TDR(Test-Driven Refactoring)——测试驱动重构”。 当然,在开发之前如果你有意识的让方法可测试,那么你写出来的函数将会是比较高质量的代码。当你的函数都是一个个可重用性高的函数之时,你将会发现,写代码其实就像堆积木一样,可以把一个大型的需求分解成无数细小的功能,很快的把需求实现。 以下是一个超大方法中的一段代码,如果你懂得怎样让这段代码编程一个可测试的方法,那么,恭喜你,你入门了。 所谓重构 如果你有耐心看到这里,你应该知道,我并非一个标题党,而这篇文章也许称为“如何在编程中应用重构的思想”更为贴切,但是我不想用这么严肃的标题。 很多编程初学者,或者有多年编程经验的人都觉得阅读别人的代码非常困难,重构更是无从谈起,他们要么对这些代码望洋兴叹,要么就是推翻从来。但是,如果我们有重构的意识,以及在编程的过程中熟悉一些代码调整和优化的小技巧,你自然而然就会培养出重构的能力。 重构,其实很简单: 把基础打牢固 多看点优秀的代码 避免复制粘贴,如果看见重复代码时应该有意识要消灭它 减少对代码生成器的依赖 在处理现有代码时尽量用重构代替重写,在重写之前一定要先重构 尽量让所有的方法都是可测试的 如果你坚持这么去做了,一段时间之后感觉自然就出来了。 重构的目的,是让你的代码更为精简、稳定、能够重用,是最大程度的让功能和业务分离。在重构的过程中,你的阅读代码的能力、写出优秀代码的能力以及系统架构能力都会稳步提升。你成为一个优秀的程序员将指日可待。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册