庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

    自从知道singleton模式这把锤子是什么样的之后,我就把很多小疙瘩也当成了钉子,时常想象偶顶着模式的光环挥舞着“万能”锤子在代码丛林中学习兰博搏斗的光辉形象~~~~。昨天读《重构与模式》的inline singleton一节,一句话点醒梦中人,singleton模式是“保证一个类仅有一个实例,并提供一个访问它的全局访问点”,原来——singleton也是全局变量啊。马教主深刻地教育过我们:全局变量在被证明无害之前都是有害。偶大汗淋漓。看看迷恋singleton的几种现象:
1、仅仅在一个地方调用到了某个singleton实例,并且对这个实例的处理代码也集中在这么一两个地方,这样的情况下你为什么要singleton?这里需要一个个全局访问点吗?我看你是为了singleton而singleton。

2、我为了性能优化啊!singleton只有一个实例,减小了创建开销。oh,我终于找到一个用singleton的充分理由了——性能。慢着,跟我读高大师的名言:“不成熟的优化是万恶之源”。你怎么知道singleton对象的重复创建是明显影响了性能?现代jvm对“短命”对象的创建代价已经非常低了。不成熟的优化不仅可能是无效的,而且也给以后重构工作带来了困难。除非有明显数据证明(分析工具而来)某个对象的重复创建是对性能影响极大,否则所谓性能优化不能成为采用singleton模式的理由。

3、有时候我们需要在系统的不同层次间传递一些共享信息,如果不采用singleton对象来提供这些共享信息,就得在调用的方法中重复地传递这些参数,这是个应用singleton模式的场景。但是,如果这些共享信息是可被修改的,或者说singleton对象不是无状态的,如果还采用singleton模式,那么你就不得不在调用的方法中从single对象取出旧信息和存入新信息,这样的重复代码将遍布的到处都是,不仅仅引入了同步访问的需要,而且出错的风险大大提高。这种情况下你还将这些信息作为方法参数传递而不是采用singleton可能更为清晰和健壮。

    singleton不仅仅是“保证一个类仅有一个实例”(这仅仅是描述),更重要的是它是用来“提供全局访问点”的(这才是它的功能),不要再迷恋这把锤子,好好利用这把锤子。

题外话:脚本语言似乎更容易滥用全局变量,javascript里可以模拟命名空间,Ruby也可以模拟类似的机制。最近写的一个比较大一点的Ruby脚本,用了几个全局变量(都是数组)用于保存状态数据,一开始没有意识到这一点,导致对全局变量的访问散落在好几个脚本文件里,RDT下看起来红通通的一片极其不爽。那么就重构吧——封装数组重构,将对这些全局数组的访问和修改操作统一到一个模块,调用全局变量的地方都引用这个模块,通过模块去操作全局变量,代码看起来清爽多了。


posted @ 2008-02-23 15:34 dennis 阅读(2097) | 评论 (5)编辑 收藏

Primitive Writes up to and including… ...are made visible to…
Object the end of a synchronized block or method a thread entering a synchronized block or method for the same object.
Volatile field a write to a volatile field any thread reading that volatile field.
Thread a call to Thread.start the newly started thread.
Thread the final write made by a dying thread any thread which successfully calls Thread.join on that thread.
Final field the initialization of a final field (but only those writes affecting the field and any object it references) any thread, provided that the constructor of the object containing the field doesn’t write the value of this anywhere eventually visible to other threads


  这些在《java并发编程实践》一书中已经有详细解释,特别是对于ReentrantLock、volatile域以及final域的讨论。今天在MenTaLguY的blog上看到这张表,摘录下。

posted @ 2008-02-23 10:44 dennis 阅读(828) | 评论 (1)编辑 收藏

    今天在pongba的邮件列表里看到了这个《免费电子书<使用开源软件——自己动手写操作系统>》
    只能说太酷了,china-pub上一直有本书《自己动手写操作系统》,牛人看到这本书使用的是商业软件做示范,于是动念想是不是能使用开源软件来实现书中所有的demo,于是就有了这本宝贵的电子书。
    项目主页:http://share.solrex.cn/WriteOS/


posted @ 2008-02-22 17:53 dennis 阅读(2893) | 评论 (3)编辑 收藏

    Buffer是一个包装了基本数据元素数组的对象,它以及它的子类定义了一系列API用于处理数据缓存。

一、属性
Buffer有四个基本属性:
1、capacity  容量,buffer能够容纳的最大元素数目,在Buffer创建时设定并不能更改
2、limit buffer中有效位置数目
3、position 下一个读或者写的位置
4、mark  用于记忆的标志位,配合reset()使用,初始值未设定,调用mark后将当前position设为值

四者关系:0 <= mark <= position <= limit <= capacity

二、API

package java.nio;
public abstract class Buffer {
public final int capacity( )
public final int position( )
public final Buffer position (int newPosition)
public final int limit( )
public final Buffer limit (int newLimit)
public final Buffer mark( )
public final Buffer reset( )
public final Buffer clear( )
public final Buffer flip( )
public final Buffer rewind( )
public final int remaining( )
public final boolean hasRemaining( )
public abstract boolean isReadOnly( );
}

支持链式调用,如:buffer.mark().position(5).reset( );
注意isReadOnly()方法决定了buffer是否可写。

三、操作
  以ByteBuffer为例,
1、访问,通过get(),get(index),其中get()从当前position位置读取,get(index)从index位置读取,不改变当前position,下面要说到的put也一样。
2、填充,通过put(byte),put(index,byte),按照绝对位置填充也是不改变当前position属性

3、flipping,试想,我们将填充完毕的buffer传递给socket输出,那么socket读取是依据position属性确定,就会从结尾后一位开始读,这样肯定是不正确的,如果要正确的读取我们先要:
  buffer.limit(buffer.position( )).position(0);
将limit设为position, 将position设为0,这个操作就叫flipping,API直接提供了这个操作:
  buffer.flip( );
特别注意,flip()方法会改变limit属性,将limit属性从capacity设置为当前position。rewind()方法与flip()类似,但是仅将position设为0,而不改变limit,通常用于重新读取已经被flip的buffer。flip()另一个注意点是,两次调用buffer的flip方法,将使得position和limit属性都为0。

4、迭代取元素:
for (int i = 0; buffer.hasRemaining( ), i++) {
myByteArray [i] 
= buffer.get( );
}

int count = buffer.remaining( );
for (int i = 0; i < count, i++) {
myByteArray [i] 
= buffer.get( );
}
ByteBuffer不是线程安全的,前一种方式适合并发访问,后一种方式效率更高。这两种方式都是一个一个取,效率都比批量取低。

5.clear()方法,将buffer重设为空状态,也就是设置limit=capacity,position=0,以便重复利用。

6.compact()方法,用于压缩buffer,这个方法在多次重复调用时是比较低效。

7.mark(),初始是未定义的,这适合如果调用reset将抛出InvalidMarkException。调用makr()后,将当前position设为mark以便reset时返回。注意,rewind( ), clear( ), and flip( )方法都将丢弃已经创建的mark。调用limit(index),positioon(index),如果index的值小于当前mark,mark也将被丢弃。

8.比较,可以通过equals()和compateTo()方法来比较两个buffer,前一个返回boolean,后一个返回0,-1,1。两个buffer equal的条件是:
1)类型相同
2)剩余元素的数目相等
3)剩余元素也一一相等

9、批量移动数据,为了更有效地进行数据传送,批量的数据存取肯定是不能少的,Buffer及其子类都有提供类似的方法,比如CharBuffer:
public CharBuffer get (char [] dst)
public CharBuffer get (char [] dst, int offset, int length)
public final CharBuffer put (char[] src)
public CharBuffer put (char [] src, int offset, int length)
public CharBuffer put (CharBuffer src)
public final CharBuffer put (String src)
public CharBuffer put (String src, int start, int end)

四、创建Buffer
    Buffer以及其子类都无法直接new,而必须把通过他们提供的工厂方法来创建。通常有两种方式:
1、allocate,例如
CharBuffer charBuffer = CharBuffer.allocate (100);
将在堆上分配一个可以存储100个字符的数组作为backing store。

2、wrap,包装一个已有的数组:
char [] myArray = new char [100];
CharBuffer charbuffer = CharBuffer.wrap (myArray);
注意,这样的方式创建的Buffer,将不会在堆上创建新的数组,而是直接利用myArray做backing store,这意味着任何对myArray或者buffer的修改都将影响到buffer或者myArray。可以通过public final boolean hasArray( )方法来判断是否拥有一个数组,通过array()方法取得这个数组。

五、复制Buffer
   其实这个复制也是“浅拷贝”,通过duplicate()方法将返回一个新创建的buffer,这个新buffer与原来的Buffer共享数据,一样的capacity,但是有自己的position、limit和mark属性。通过asReadOnlyBuffer()方法复制的buffer与duplicate()类似,但是是只读的,不能调用put。比较特别的是slice()方法,故名思议,类似切割一个Buffer出来,与duplicate类似,但是它将从原来Buffer的当前position开始,并且capacity等于原来Buffer的剩余元素数目,也就是(limit-position)。

posted @ 2008-02-22 14:31 dennis 阅读(6622) | 评论 (1)编辑 收藏

    读blog看到的一个小技巧,原文在这里
    我们常常处理这样的代码:
name=person?person.name:nil

    取某个对象的一个属性,先判断对象是否为nil,不是nil就返回对象属性,否则返回nil。这样的代码写多了比较恶心,是否有比较有趣的方式来减少代码?作者给出了一段代码:
module ObjectExtension
 
def nil_or
  
return self unless self.nil?
  o 
= Object.new
  
class << o
    
def method_missing(sym, *args); nil; end
  end
  o
  end
end
class Object
  include ObjectExtension
end

   上面的代码为Object加入了扩展,为每个对象实例增加了一个nil_or方法,分析下这个方法:如果对象不为nil,马上返回self(也就是对象本身),否则生成一个新的对象,这个对象通过method_missing机制将所有的方法调用都返回nil(原文是用Class.new,生成的类将不会被GC,这里采用回复给出的方案,生成对象,在对象的metaclass上做method_missing)。那么现在代码可以写成:
    name=person.nil_or.name
是不是相当的DSL呢?

posted @ 2008-02-19 13:39 dennis 阅读(838) | 评论 (1)编辑 收藏

修正了很多bug,我关注的 JRUBY-1686也得到了修正,距离1.1的发布不远了。

The JRuby community is pleased to announce the release of JRuby 1.1 RC 2

Homepage: http://www.jruby.org/
Download: http://dist.codehaus.org/jruby/

JRuby 1.1RC2 is the second release candidate of JRuby 1.1.  JRuby 1.1
represents a concerted focus on speed and refinement.  ruby code can
completely compile in an Ahead Of Time (AOT) or Just In Time (JIT) mode;
yielding a faster ruby!  It also uses less memory than our previous releases.

We need people to download JRuby 1.1RC2 and give us feedback.  Test your
applications and help us make JRuby 1.1 a great release.

Highlights:
- 260 issues resolved since JRuby 1.1RC1
- Large IO refactoring
- Memory improvements for JIT'd methods:
  - Control total number of methods JIT'd
  - Support a JIT cache between runtimes to return permgen
  - Reduce codesize of generated methods (50-70% reduction)

posted @ 2008-02-18 09:43 dennis 阅读(1043) | 评论 (0)编辑 收藏

    要在JRuby中实现java接口,接口include进来,实现接口方法即可,例如实现java.lang.Runnable接口做线程处理:
require 'java'
include_class 'java.lang.Runnable'
class
 TestRunnable
    include Runnable
    
def initialize(name)
      @name
=name     
    end
    
def run
      puts 
"hello,"+@name
    end
end

    要在JRuby中继承抽象类,实现其中的抽象方法,方法稍微麻烦点,需要cglib,到这里下载cglib-nodep-2.1_3.jar,写个通用库abstract_class.rb方便处理:

load 'cglib-nodep-2.1_3.jar'

class Object
  include Java
  include_class 
"net.sf.cglib.proxy.Enhancer"
  include_class 
"net.sf.cglib.proxy.NoOp"
  
  
class <<self
    
def method_missing(mname, *args, &block)
      unless mname 
== :abstract_impl and respond_to?(:java_class) and JavaLang::reflect::Modifier.isAbstract(JavaLang::Class.for_name(java_class.name).modifiers)
        super
      
else
        generate_abstract_impl(args, 
&block)
      end
    end
    
    private 
    
    
def generate_abstract_impl(args, &block)
      factory 
= Enhancer.new
      factory.setSuperclass(java_class)
      factory.setInterfaces(java_class.interfaces.to_java(
"java.lang.Class"))
      factory.setCallback(NoOp::INSTANCE)
      
      object_args 
= args.map { |arg| Java.ruby_to_java(arg) }
      class_arguments 
= object_args.map {|arg| arg.java_class}.to_java("java.lang.Class")
      generated_class 
= factory.create(class_arguments, object_args.to_java("java.lang.Object"))
      
      ruby_class 
= Class.new(generated_class.class)
      ruby_class.class_eval(
&block)
      
      
return ruby_class.new(*args)
    end
  end
  
end

    使用的话,require一下abstract_class,例如我们要继承java.util.TimerTask,实现其中的run方法:
$:.unshift File.join(File.dirname(__FILE__),'.')
require 
'java'
require 
'abstract_class'
import java.util.TimerTask
import java.util.Timer
timer_task 
= TimerTask.abstract_impl do
  
def run
    puts 
"timer task"
  end
end

Timer.new.schedule(timer_task, 
10001000)



posted @ 2008-02-15 14:34 dennis 阅读(1510) | 评论 (1)编辑 收藏

    过去写的那个利用google在线翻译的小脚本工具一直在用,今天用的时候,突然想,我今年不是想加强下英语学习吗?那么把每天查过的单词保存下来,每天早上或者上班空闲期间花那么几分钟记忆复习下这些单词不是很好,毕竟技术性文章翻来覆去运用的单词就那么多,过去没有注意积累,导致常常还得重新查,所谓提高也就放在口头上了。说改就改,脚本语言改起来就是容易:

#利用google在线翻译,翻译中文<->英文
#
author dennis
#
version 0.2
require 'net/http'
$contents
=Hash.new
$dir
="F:/English/"
now
=Time.now
$today
="#{now.year}#{now.month.to_s.rjust(2,'0')}#{now.day.to_s.rjust(2,'0')}"
def translate
  txt
=STDIN.gets
  
if txt.strip=='e' or txt.strip=='exit'
    
#退出前保存
    if $contents.size>0 then
      File.open(
"#{$dir}#{$today}.txt","a+") do |file|
        $contents.each {
|key,value| file.write(key.ljust(20)+value.ljust(20)+"\n")}
      end
    end
    exit
  end
  temp
=txt.split(' ')
  
if temp[1]=='1' or temp.size==1
    langpair
='en|zh-CN'
  
else
    langpair
='zh-CN|en'
  end
  begin 
    
#使用代理  
    #$proxy_addr = '192.168.9.25'
    $proxy_port = 8081
    $proxy_user
='test'
    $proxy_passwd
='test'
    
if $proxy_addr
     response 
= Net::HTTP.Proxy($proxy_addr,$proxy_port,$proxy_user,$proxy_passwd).post_form(URI.parse("http://translate.google.com/translate_t"),{'text'=>temp[0],'langpair'=>langpair}) 
    
else
      response 
= Net::HTTP.post_form(URI.parse("http://translate.google.com/translate_t"),{'text'=>temp[0],'langpair'=>langpair}) 
    end
    response.body 
=~ /<textarea.*?id=suggestion>(.*?)<\/textarea>/
  rescue  StandardError 
=>e
    $stderr.
print "错误:"+e
  
else
    result 
= $1 
    puts 
'翻译内容:'+temp[0]
    puts 
'google返回:'+result if result
    $contents[temp[0]]
=result
    puts 
'-------------------退出请打e或者exit---------------'
    translate
  end
end
translate


posted @ 2008-02-14 11:15 dennis 阅读(527) | 评论 (0)编辑 收藏

    习惯了,如果没有过完春节,感觉这一年还不算过去。年过完了,今天就开始上班了,昨天一早3点多到的广州,太冷了。怎么说呢?先来个总结吧,2007年都干了什么,2008年想干什么,工作上的。
    首先是折腾——换工作,11月份辞职到星网,然后干了两个月,被mryufeng老大召唤到了广州,折腾来折腾去终于是稳定下来,要安心干活了。然后今年学习了Erlang,读了sicp前三章,开始学习网络和并发编程,对Ruby也有所深入,不过对unix编程还是进展不多。本来是计划能看完sicp和豆瓣上列着那几本,后来折腾的人精神都散了,没什么心思读书。今年的计划,首先是sicp第四章要继续读,习题继续做,读完这个还有《算法导论》等着我;然后是Erlang,按鱼老大的意思是年后要搞一搞,怎么也得再下番功夫,主要是otp这方面我还不是很熟悉。然后是想去读读几本经典的unix网络编程方面的,动手做做。最后就是继续关注感兴趣的那么几个主题:osgi、Ruby、高性能网络服务器的开发。
   自从做了这行,貌似人变的越来越枯燥了,新一年要多关心身边的人,多关心老爸老妈,多关心弟弟,多听听音乐,幸好我还是保持着看电影和看球的习惯,希望家人朋友都身体健康(这比啥都重要),希望inter五月份能去莫斯科,希望我能有更多的耐性和坚持。也祝福看blog的你身体健康,工作顺利,恭喜发财咯,哈哈。
 

posted @ 2008-02-13 11:30 dennis 阅读(471) | 评论 (2)编辑 收藏

    1月28日,24岁的深圳打工仔杜登勇孤独地走在京珠高速公路上,他已经步行了18个小时。25日,他得知女朋友在回家途中被困湖南株洲,而且生病了。 27日,他借了500元,坐火车从深圳来到广州,当天乘汽车到了韶关。当晚,京珠高速已封。杜登勇最后决定,连夜步行,前往400多公里外的株洲。

    其实这世界从来不缺乏温暖的瞬间。


posted @ 2008-01-31 19:59 dennis 阅读(414) | 评论 (1)编辑 收藏

仅列出标题
共56页: First 上一页 29 30 31 32 33 34 35 36 37 下一页 Last