浅谈设计模式

冯老师那天跟我提到设计模式,我觉得还应该很有意思吧,我就逛了逛贴吧和社区。下面就听我这个计算机系的女学渣讲述一下我的学习心得吧。

我:什么是设计模式呢?

师兄:设计可以理解为设计方案,模式呢?是解决问题解决多了,除了一套行之有效方案

我:我对于设计模式的理解,给大家举个例子,以我们的电脑为例子吧,他是一个对象不过有点复杂,是由几千个其它对象组成的,包括主板,硬盘,内存,显卡,网卡,CPU。笔记本在制造的时候,制造商搜集所有零件将它们组装起来。当然啦我们也是可以的,我们可以到京东买一些零件来组装电脑,电脑商并不在乎这些零件是怎么生产的,当然他们要知道零件质量过硬,比如苹果公司会把在各个国家生产的零件搜罗到一起,但是他并不在乎他是怎么生产,制造商只会关心如何通过不同的方式将不同的零件组装起来,以便生产出不同型号的电脑。每种型号的电脑都会有他们独特的设计。设计是需要深思熟虑的,以后的生产只需要遵循设计就好啦。这么一说,加入我们是软件厂商,可以用不同组件来创造出不同的软件。我们尝试着用一种面向对象的方式来开发我们的软件,利用OOD原则来让我们的代码更容易管理、重用和扩展。就像你上面提到的那些相同的问题,如果我们预先就有一些良好的设计,那是不是很棒呢?还有个好消息,我们并不需要自己想。这么多年以来,遭遇同样问题的人们早已发现了许多很棒的解决方案,而且把它们标准化过了。我们管这些方案叫设计模式。

设计其实与生活息息相关,如师兄提到的如果迎宾路堵车,我们该如何解决呢?其实这个问题我们程序的高并发一样。

下一期我们来谈谈这个问题,并谈谈基础的设计问题和解决方案。小女不才,还望包涵。

 

 

 

 

关于程序开发的一些小想法 part1

一、由scope(“prototype”)引发

  1. 昨天峰哥说了一个tag上必须加@scope(“prototype”)这样一个注解 。这个注解是将该类以多例的方式注入spring容器,能够解决多线程访问的线程安全问题;原本v52以前我们的action上也有这样的注释——因为Struts是类级别共享资源,所以在多线程访问时同样会有线程安全问题;修改为spring-mvc之后 ,spring-mvc是方法级别共享资源。所以不存在线程安全问题,因此不用加这样的注解。但是@scope(“prototype”)标注的类不应该被@autowired注入到其他类中,否则当有循环依赖的情况发生时,spring容器会无法初始化,抛出BeanCurrentlyInCreationException异常(参考开涛的博客-跟我学spring3  第三章3.2)
  2. 那么什么是循环依赖?

以maven项目为例,a项目依赖b,b项目依赖c,c项目依赖a,形成了一个闭合环就是循环依赖:

依据maven的生命周期,a项目在进行打包或者安装操作时,需要根据依赖先打包或者安装b,b项目在打包或安装时,需要先打包或安装c,c项目在打包或者安装时,需要先打包项目a;此时就会无限的循环下去直至内存溢出。

以上这种循环依赖无解。如果发生这种情况,一定是我们抽象的不对,假如a,b,c三个项目有共有的依赖,为什么不把他们抽象出来一个单独的项目d来由a,b,c共同依赖呢?

以上是使用maven项目构建时的一个小案例,上次峰哥再讲maven的时候我也提及过,我们在使用maven构建项目时可以考虑按功能抽象,也可以考虑按类型(core与模板分离)抽象。

循环依赖在任何具有依赖关系的技术以及体系中都存在。我们知道开发中如果表示类与类的关系时有以下几种表达方式:

泛化 、继承、实现、依赖、关联、聚合、组合

(参考文档http://www.cnblogs.com/olvo/archive/2012/05/03/2481014.html)

比如我们在将对象实例化并注入spring容器中时,会有一些相应的依赖关系。

 因为scope是singleton的 所以这种循环依赖没有报错(会在实例化过程中提供标识,全容器可见:参考开涛博客第三章3.2)假设此时有任意一个被注入的类scope是prototype的,就不会提供这种全容器可见的标识,因此会抛出上述异常。

因此在设计类的时候,也可以抽取公共部分把循环依赖改变成单向的依赖,这样反而更符合逻辑,更符合开发的规范。

我们从上面的描述,可以理解到面向对象编程存在依赖关系时,应尽量避免循环依赖,从类结构、包结构、项目结构都要尽量避免有循环依赖的出现。

在v62的开发时,会引入一个测试代码质量的工具jdepend,这个大家可以作为一个研究院的课题研究jdepend的使用。

二、我的思考

  1. 接口实现类空实现

我们在写一个功能的时候,包括我也经常做一下操作:在orderController中调用orderManager的一个没有的方法,然后直接在接口中直接增加一个方法,导致StoreOrderManager需要增加一个空实现的方法。

先说说这样做的缺点:会给我们的代码增加很多的冗余,我们的代码本身就很臃肿,一个类1500行,可读性差,可维护性差。

在开发过程中,我们要优先遵循最基本的六大开发原则:开闭原则、接口隔离原则(依赖最小接口)、单一职责原则(一个类要有一组相同功能)、依赖倒置原则(依赖于抽象,不依赖具体)、里氏替换原则(具体实现应该可以互相替换而不被依赖者察觉)、迪米特原则(尽少知道)

还有其他各种原则:我整理了一下,认为这两个比较重要:apo(Avoid Premature Optimization避免提前优化)原则、dry(Don’t repeat yourself)原则

从哪些方面来遵守这样的原则呢?

我们首先要抽象一个service层。我发现,我们之前的代码由于各种历史原因,有的校验在controller中进行,并且有的controller中还包含了部分业务逻辑,这样做是不合理的。Controller是一个控制中心,只负责各种功能性代码的调用与执行;而manager中除了操作数据库,还要处理业务,首先就违背了单一职责、接口隔离、迪米特等原则,再深层次的考虑,会使我们的维护变得困难,对我们功能的扩展造成麻烦。

因此我的构想,在未来的6.3 或者7.0版本(过年前后),所有人这个时间都不要提交代码,大约7-10天的一个周期,从包结构上以模块划分、降低包之间的耦合,从类代码上抽象出一个service层,controller中只写调度,service层只写逻辑,dao层只操作数据库—-从各个角度看,这样更合理;以前我工作的公司都是这么做的,从可用性来讲也存在一定的可行性。在重构期间我们遵循上述八条开发原则,将接口拆分成最小单位,实现拆成最小单位,职责拆成最小单位,从结构上来对我们的代码进行整体优化。

  1. 在讲一点开发体会。

1)      关于脑回路:

举个例子,项目经理说,李冰长得好漂亮。

这个时候,郑皓的脑回路是这样的:

李冰—-漂亮—-泡她

我的脑回路

李冰—-漂亮—-范冰冰—-李晨—-有好车—-阿斯顿马丁—-帕加尼—-意大利—-AC米兰—-巴乔……

所以我们一个团队在共同开发的一款产品的时候,会遇到一个问题,项目经理分配的任务有可能没有按照预期的方向进行。所以此时要实时沟通。但是实时沟通的成本太高,所以我们要基于约定来进行开发。约定就包括了流程图,pdm,原型,优秀的开发者还会涉及uml图。

约定好这些之后,才可以进行开发,细节上可以讨论,但是大原则是基本不变的。这有助于提高我们的开发效率。同时文档齐全,交付时连文档一起交付。

开发完成后还要写一个简单的功能说明文档。

失败的案例。

我们所有的软件工程讲的这些工程学的东西,无非是为了更好更快更高质量的生产,那么放入到程序中质量的体现就是代码的复用性以及扩展性(这里还要加上易用性)。

我在设计“装修”功能时,设计的时候,充分的考虑到了可能的扩展,提前预留了一些扩展的字段;现在看来,这种做法是违反我们开发的原则的(避免提前优化)。

在设计的时候为了开发的方便性,暴露了很多内部逻辑给前端人员,这样用起来很困难。

封装可以解决易用的问题。

预告,下次part2将maven多模块项目的真实面目,敬请期待。

悲观锁与乐观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

转自:http://blog.csdn.net/hongchangfirst/article/details/26004335

看了很多,发现这个总结,太贴切。不知道怎么加自己观点了,只能实际运用加观点了。这里直接转载~

eclipse下svn多分支项目切换及合并

Javashop bug同步解决方案

一、核心问题

在我们处理bug的过程中,经常发生以下场景:解决trunk版bug的同时需要解决v6.1.1版本。

这时我们就要手动切换workspace,手动覆盖;这样搞起来很麻烦;

而当定版之后,在trunk中解决了大量的bug,如果一个一个的去tags版中修复,工作量不亚于重新解决一遍bug。

造成这个问题的主要原因是,我们的svn使用的不够熟练,没有把svn的用法完全提现出来。

二、解决方案

(使用eclipse的subversion插件中的 切换以及合并结合)

场景:v6.1.1定版后第三天,trunk版又解决了大量的bug。

首先,我们要把trunk版的代码down下来,并且保证本地版本最新。

 

Step1:在trunk的workspace中 右键javashop项目-team-切换:

 

Step2:在弹出dialog中输入需要切换的分支的url;例如

svn://newsvn.javamall.com.cn/product/javamall/tags/release_6.1.1/javashop

 

Step3:

切换完分支之后,右键team-合并

 

Step4:

图中的选项含义分别是:

1) 从主干合并到分支

2) 从分支合并到主干

3) 将主干上的修改合并到分支(使用collabnet)

4) 合并2个分支到主干

5) 从主干到分支,手工指定不需要合并的修改

6) 从主干到分支,手工指定要合并的修改

选择第一个,会弹出如下窗口:

 

 

点击finish

 

有变化的文件  有冲突的文件都会显示出来。Svn会自动合并能够合并的代码,而由冲突的则需要手动处理。手动处理完成后,提交即可。

关于maven 项目install报错的问题以及解决办法

  • 问题

近日有很多童鞋反映,javashop 6.1及6.0版本执行mvn install时报错,报错信息大致如下:

经过测试发现,在eclipse环境下运行mvn install正常,而shell下使用指令mvn install则会报错;分析后发现是因为eclipse环境能够识别我们的java resourse,而shell或者命令行下无法识别我们的java resource。

  • 标准的maven项目的目录结构是:src/main/java  
  • 标准的maven项目的test目录结构是:src/test/java
  • 标准的maven项目的配置文件路径是:src/main/resources

当我们不使用这种标准的maven项目结构时,在shell下 执行mvn install 就会发生找不到类或者找不到资源的问题。

  • 解决办法
  • 引入build-helper-maven-plugin插件

    <!– 增加更多的Source和Test Source目录插件 –>
    <plugin>
         <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
        <version>1.12</version>
    </plugin>

  •  配置需要添加的资源目录

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/eop</source>
<source>src/base</source>
</sources>
</configuration>
</execution>

<execution>
<id>add-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/base</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.html</include>
<include>**/*.css</include>
</includes>
</resource>
<resource>
<directory>src/eop</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.html</include>
<include>**/*.css</include>
</includes>
</resource>
</resources>
</configuration>
</execution>

</executions>

</plugin>

  • 配置eclipse lifecycle-mapping

<pluginManagement>
<plugins>
<!–This plugin’s configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. –>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.codehaus.mojo
</groupId>
<artifactId>
build-helper-maven-plugin
</artifactId>
<versionRange>
[1.12,)
</versionRange>
<goals>
<goal>add-source</goal>
<goal>add-resource</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>

配置完成之后再试试,看看是不是问题已经解决了?

如果您购买了javashop 的61版,我们已经更新了pom.xml文件哦,只需要更新pom文件即可。

  • 另外在mvn install b2b2c 之前  需要先mvn install javashop

从零开始写微信小程序(一)—–开发环境搭建

最近微信小程序相关的新闻铺天盖地,各种言论诸如“app将死,微信当立”,搞的笔者十分好奇,于是在一个阳光明媚的早晨,对微信小程序一探究竟。

首先下载开发环境:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html

下载并安装完成后,启动开发工具:

由于我已经有一个项目了,所以图中有个demo2。

点击添加项目,会让你填写appid,项目名称,项目路径。由于目前正在内测,所以如果没有appid可以选择无appid.项目名称和项目路径自填。

如果勾选了创建quickstart选项,则会在工作空间创建一个简单的项目:

点击添加项目,一个简单的helloword程序就搭建成功了。

下篇文章,将为您介绍微信小程序开发的基本配置,敬请期待。

微信小程序官网地址:https://mp.weixin.qq.com/debug/wxadoc/dev/

本文有javashop提供,享有一切相关版权;转载请注明出处。

javashop唯一官网地址:多用户商城系统

sql server 注意事项(二)

sql server与mysql最主要的区别是 sql server对sql的语法要求非常严格 ,而mysql对sql的语法容错较好。

因此当我们在写一些包含分组(group by)的语句时 ,select 后面的字段 必须包含在group by子句或者聚合函数中.

select * from es_order group by member_id;

在mysql中可以正确执行;而在sqlserver中,则会报错:

因此我们在写sql语句时,为了更好的兼容性,一定要写的严谨一些;而多数情况下,不兼容多是因为sql语句不够严禁造成的。

另外,mysql、oracle、sqlserver不兼容也有可能是各自的内置函数不同导致,例如:

mysql和oracle共同支持instr()函数,而sqlserver不支持此方法,如果需要使用按指定字段、指定顺序排序的话,则需要使用charindex函数

sql server 注意事项

sql 函数使用注意事项:

sql 查询 使用函数count avg 等,查询数据库时,如果输入order by 则必须同样将函数中的字段进行分组(group by)

例如:

错误的写法:select count(0) from es_sellback_list where type=? AND tradestatus=? order by id

正确的写法:select count(0) from es_sellback_list where type=? AND tradestatus=? group by id order by id

或者 select count(0) from es_sellback_list where type=?

否则会报异常“ORDER BY 子句中的列 “‘cloum’ 无效,因为该列没有包含在聚合函数或 GROUP BY 子句中。

SQLServer分页必须order by 如果不包涵order by 自动id 降序,否则异常

在使用我们的page时,用到分页,如果要分页的表没有id 字段,那么需要手动添加一个order by cloum,否则异常

除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、

sqlserver要求,没有指定TOP,那么要求不可以使用ORDER BY,因为排序是消耗资源的

 

从Integer比较引发的一起血案.

某日,javashop的小李在检查代码时,发现了一个比较严重的问题.

笔者看完之后很纳闷,为啥Integer的比较不能用==呢?

我们平时声明一个int类型的a 和一个int类型的b,完全可以使用==来比较,虽然Integer是引用类型,但是他一定是做了什么处理,不然我在比较的时候,为啥得到的结果和预期的时候一致呢?

带着这个疑问,笔者做了一个实验:

两次比较的结果竟然不一样,笔者瞬间就惊了个呆,原来自己的理解一直是错的,实在是愧对老司机这个称号!

但是为什么第一次比较的结果是一样的呢?笔者又做了一次实验,发现在-128到127这个范围之间,使用==比较时结果是相等的,而超出了这个范围,使用==比较往往得不到预期的结果.

一定是做了什么特殊的处理,Integer和其他妖艳贱货的引用类型不一样!

于是笔者仔细查看了Integer源码,发现了这样一段代码:

原来在对Integer进行赋值操作的时候 即Integer a=1时,进行了装箱操作,使用的是上图中的方法.当传入的值在-128-127之间是,会从java的缓冲池获取一个对象.而==符号比较的是引用类型的地址,返回缓冲池对象时,地址相同,所以==会生效.

同样的道理,我们在使用String类型比较的时候

String a=”abc”;

String b=”abc”;

a==b返回的结果同样是true,因为abc也是放入了字符串常量池,使用上述方法赋值时引用地址相同;反之,如果在对Integer赋值时使用

Integer a=new Integer(1);

Integer b=new Integer(1);

a==b的返回结果就变成false啦!

所以在对引用类型进行比较时,应使用equals()方法,而不要使用==符号

服务器tomcat自动停止问题

Linux下面有个机制叫OOM killer(Out Of Memory killer,这个东西会在系统内存耗尽或者即将耗尽的情况下跳出来执行,选择性的干掉一些进程以求释放一些内存。

可能会出现的问题就是,某天tomcat无缘无故停掉了,或者是 某天机器突然登不上了,能ping通,但是ssh死活连不了。原因是tomcat/sshd进程被OOM killer干掉了。在系统的日志中通常会有下面的打印日志:Out of memory: kill process 959 (sshd) score 55 or a child。 

1.       OOM什么时候出现?

我们在用户空间申请内存时,一般使用的是malloc,是不是当malloc返回为空时,没有可以申请的内存空间了就会返回呢?答案是否定的。在关于malloc的申请内存的机制中有下面的一段描述:

By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. This is a really bad bug. In case it turns out that the system is out of memory, one or more processes will be killed by the infamous OOM killer. In case Linux is employed under circumstances where it would be less desirable to suddenly lose some randomly picked processes, and moreover the kernel version is sufficiently recent, one can switch off this overcommitting behavior using a command like:

上面的描述中说明了在Linux中当malloc返回的是非空时,并不代表有可以使用的内存空间。Linux系统允许程序申请比系统可用内存更多的内存空间,这个特性叫做overcommit特性,这样做可能是为了系统的优化,因为不是所有的程序申请了内存就会立刻使用,当真正的使用时,系统可能已经回收了一下内存。但是,当你使用时Linux系统没有内存可以使用时,OOM Killer就会出来让一些进程退出。

Linux下有3种Overcommit的策略(参考内核文档:vm/overcommit-accounting),可以在/proc/sys/vm/overcommit_memory配置(取0,1和2三个值,默认是0)。

(1)0:启发式策略,比较严重的Overcommit将不能得逞,比如你突然申请了128TB的内存。而轻微的overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多

(2)永远允许overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。

(3)永远禁止overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。

/proc/sys/vm # cat overcommit_ratio

50

当然我可以修改proc//oom_adj的值,这里的默认值为0,当我们设置为-17时,对于该进程来说,就不会触发OOM机制,被杀掉。

echo -17 > /proc/$(pidof sshd)/oom_adj

这里为什么是-17呢?这和Linux的实现有关系。在Linux内核中的oom.h文件中,可以看到下面的定义:

/* /proc//oom_adj set to -17 protects from the oom-killer */

#define OOM_DISABLE (-17)

/* inclusive */

#define OOM_ADJUST_MIN (-16)

#define OOM_ADJUST_MAX 15

这个oom_adj中的变量的范围为15到-16之间。越大越容易被kill。oom_score就是它计算出来的一个值,就是根据这个值来选择哪些进程被kill掉的。

总之,通过上面的分析可知,满足下面的条件后,就是启动OOM机制。

1) VM里面分配不出更多的page(注意linux kernel是延迟分配page策略,及用到的时候才alloc;所以malloc + memset才有效)。

2) 用户地址空间不足,这种情况在32bit机器上及user space超过了3GB,在64bit机器上不太可能发生。

2     当该机制被触发后,会让什么样的进程退出?

只要存在overcommit,就可能会有OOM killer。 Linux系统的选择策略也一直在不断的演化。我们可以通过设置一些值来影响OOM killer做出决策。Linux下每个进程都有个OOM权重,在/proc//oom_adj里面,取值是-17到+15,取值越高,越容易被干掉。  最终OOM killer是通过/proc//oom_score这个值来决定哪个进程被干掉的。这个值是系统综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime – start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低。总之,总的策略是:损失最少的工作,释放最大的内存同时不伤及无辜的用了很大内存的进程,并且杀掉的进程数尽量少。  另外,Linux在计算进程的内存消耗的时候,会将子进程所耗内存的一半同时算到父进程中。

最后还是说一下使用需要注意的事项:

1、内存首先要足够基本进程的正常运行,以防被无缘无故kill

2、要对tomcat的设置进行优化,尤其是对内存的控制