精华内容
下载资源
问答
  • 关于python的开发环境配置,请看我之前的博客: ...当然初学者可以使用IDLE的shell来编写代码,对于初学者来说很好用。 第一部分列表 给一个创建名为movies列表的例子...movies = ['The Holy Grail',1975,'The life o

    关于python的开发环境配置,请看我之前的博客:
    http://blog.csdn.net/qq_22499377/article/details/78651840
    当然初学者可以使用IDLE的shell来编写代码,对于初学者来说很好用。

    第一部分列表

    给一个创建名为movies列表的例子:

    movies = ['The Holy Grail',1975,'The life of Brian',1979,'The Meaning of Life',1983]

    列表常用的BIF(BIF指内置函数):

    • print()BIF的作用:在屏幕上显示列表内容
    • len()BIF的作用:得出列表中有多少数据项

    列表常用的方法:

    • append()方法的作用:在列表末尾增加一个数据项
    • pop()方法的作用:从列表末尾删除数据
    • extend()方法的作用:在列表末尾增加一个数据项集合
    • remove()方法的作用:在列表中找到并删除一个特定的数据项
    • insert()方法的作用:在某个特定的位置前面增加一个数据项

    附一张具体使用的示例:
    这里写图片描述
    for循环处理任意大小的列表:

    for 目标标识符 in 列表:
            列表处理代码

    示例:
    这里写图片描述
    划重点:
    1.python的变量标识符没有类型。
    2.在同一个列表中可以存储任意类型的数据。
    3.在python中创建一个列表时,解释器会在内存中创建一个类似数组的数据结构来存储数据,数据项自下而上堆放(形成一个堆栈),堆栈中的第一个槽编号是0,第二个槽编号是1,第三个槽编号是2,以此类推。所以使用print(movies[1])访问某一个数据项。
    4.当然在列表中可以嵌套列表,用if_else或者用isinstance函数来判断每一项是否为列表。
    5.for循环允许迭代处理一个列表,这通常比使用一个等价的while循环更方便。

    第二部分函数

    Python中的函数是一个命名的代码组,如果需要,还可以有一个参数表(可选)。要用def语句定义python函数,为函数提供一个函数名,并在括号里指定一个参数表,参数表可以为空。
    以下是定义函数的标准形式:

    def 函数名(参数):
            函数代码组

    示例:显示带有嵌套列表的列表内容,要求功能中可以选择是否缩进,显示结果整体从左边缩进多少位和可选的结果输出位置。

    def print_lol(the_list, indent=False, level=0, out=sys.stdout):
        for each_item in the_list:
            if isinstance(each_item, list):
                level =level + 1
                print_lol(each_item, indent, level, out)
                level = level - 1
            else:
                if indent:
            # 使用level的值来控制使用多少个制表符
                    for tab_stop in range(level):
                        print('\t', end='', file=out)
                print(each_item, file=out)

    示例代码中indent,level,out都有缺省值,使用这个函数可以自己指定参数,也可以直接使用缺省值。indent代表是否使用缩进,默认是不使用缩进。level代表结果整体从左边缩进多少位,默认是0,也就是整体是从左边起始位置开始缩进的。out代表结果输出位置,默认是标准输出。
    常用的BIF说明:
    这里写图片描述
    划重点:
    为了增强代码的健壮性,一般定义函数时都会给参数设置缺省值,如果定义函数时为函数参数提供一个缺省值,这个函数参数就是可选的。

    第三部分处理在文件中的数据

    python中的基本输入机制都是基于行的:从文本文件向程序读入数据时,一次会到达一个数据行。
    示例:文件去http://python.itcarlow.ie/resources.html下载Chapter 3 & 4: sketch.txt,处理文件的数据并从数据中抽取出两个人说的话,并打印出来。

    """
    处理一个文本文件,将整理Man和Other Man说的全部的话,然后打印出来。
    """
    # 用"try/except"这里指定了IOError,是为了避免读取数据时产生错误的情况
    try:
        # 打开一个文件,传入的参数是文件在电脑里存放的路径
        data = open('D:\\pythondata\\HeadFirstPython\\chapter3\\sketch.txt')
        # python是一行一行读取的,所以要迭代
        for each_line in data:
            # 用"try/except"这里指定了ValueError,是为了避免分隔数据时产生数据和分隔符不匹配的情况
            # pass的意思就是忽略这样的错误,代码继续执行
        # split(':',1)是为了处理一行文本中有两个冒号的情况,我们只以第一个冒号作为分隔符
            try:
                (role,line_spoken)=each_line.split(':',1)
                print(role, end='')
                print(' said: ', end='')
                print(line_spoken, end='')
            except ValueError:
                pass
    
            data.close()
    except IOError:
        print('The data file is missing!')

    划重点:
    1.使用”try/except”可以让你关注代码真正需要做的工作,而且可以避免向程序增加不必要的代码和逻辑。
    2.数据不符合期望的格式时会出现ValueError。
    3.数据无法正常访问时会出现IOError(例如你的数据文件已经被移走或者重命名)。
    4.pass语句就是python的空语句或null语句,它什么也不做。

    第四部分将数据保存到文件

    使用open()BIF打开文件时,可以指定使用什么访问模式。默认地,open()使用模式r表示读,所以不需要专门指定r模式。
    这里写图片描述
    使用访问模式w时,python会打开指定的文件来完成写。如果这个文件已经存在,则会清空它现有的内容,也就是完全清除。要追加到一个文件,需要使用访问模式a。要打开一个文件来完成写和读(不清除),需要使用w+。如果想打开一个文件完成写,但是这个文件并不存在,那么首先会为你创建这个文件,然后再打开文件进行写。
    示例:还是读取sketch.txt,然后将输出文件分别命名为man_data.txt(存储一个人讲的话)和other_data.txt(存储另一个人讲的话)。数据文件打开后都要确保将其关闭。

    man = []
    other = []
    try:
        data = open('D:\pythondata\HeadFirstPython\chapter3\sketch.txt')
        for each_line in data:
            try:
                (role,line_spoken)=each_line.split(':',1)
                line_spoken=line_spoken.strip()
                if role == 'Man':
                    man.append(line_spoken)
                elif role == 'Other Man':
                    other.append(line_spoken)
            except ValueError:
                pass
    except IOError:
        print('The datafile is missing!')
    finally:
        data.close()
    try:
        man_file = open('D:\pythondata\HeadFirstPython\chapter3\man_data.txt','w')
        other_file = open('D:\pythondata\HeadFirstPython\chapter3\other_data.txt','w')
        print(man, file= man_file)
        print(other, file=other_file)
    except IOError:
        print('File error.')
    finally:
        man_file.close()
        other_file.close()

    “腌制”数据
    python提供了一个标准库,名为pickle,它可以保存和加载几乎所有python数据对象,包括列表。
    使用pickle的方法:导入pickle模块,然后使用dump()保存数据,以后某个时间使用load()恢复数据。处理腌制数据时的唯一要求是必须以二进制访问模式打开这些文件。
    示例:将[1,2,’three’]保存数据,然后再恢复数据。

    import pickle
    # “b”告诉python以二进制模式打开数据文件
    with open('mydata.pickle','wb') as mysavedata:
        pickle.dump([1,2,'three'],mysavedata)
    with open('mydata.pickle','rb') as myrestoredata:
        a_list = pickle.load(myrestoredata)
    
    print(a_list)

    划重点:
    1.不论出现什么错误都必须运行某些代码,比如关闭文件的代码,可以向try语句的finally组增加代码。
    2.with语句会自动处理所有已打开文件的关闭工作,即使出现异常也不例外。with语句也使用as关键字。
    3.print()BIF的file参数控制将数据发送/保存在哪里。
    4.标准库的pickle模块允许你将python数据对象保存到磁盘以及从磁盘恢复。pickle.dump()函数将数据保存到磁盘,pickle.load()函数从磁盘恢复数据。
    “腌制”数据时,“腌制”代表将数据对象保存到一个持久存储的过程。“解除腌制”代表从持久存储中恢复一个已保存的数据对象的过程。

    第五部分处理数据

    排序有两种方式:
    1.原地排序
    原地排序是用排序后的数据替换原来的数据。对于列表,sort()方法会提供原地排序。
    2.复制排序
    复制排序是原数据的顺序依然保留,只是对一个副本排序。在python中,sorted()BIF支持复制排序。
    默认地,sort()方法和sorted()BIF都会按升序对数据排序。要以降序对数据排序,需要向sort()或sorted()传入参数reverse=True,python会负责具体处理。
    示例:文件去http://python.itcarlow.ie/resources.html下载Chapter 6 updated TXT files 。处理文件中运动员的计时数据,要把错误数据进行转换,并显示一个运动员的最快的前三个时间。

    """
    这个函数的功能是找到错误数据,替换成正确数据。
    """
    def sanitize(time_string):
        if '-' in time_string:
            splitter = '-'
        elif ':' in time_string:
            splitter = ':'
        else:
            return  time_string
        (mins, secs) = time_string.split(splitter)
        return mins+'.'+secs
    """
    这个函数的功能是读取文件中的数据,返回的是一个运动员的信息(用字典来存储数据)和一个运动员跑步最快的前三个时间的信息。
    """
    def data_control(file_path):
        try:
            file_content = []
            with open(file_path) as read_file:
                data = read_file.readline()
                file_content = data.strip().split(',')
            sarah_data = {'sarah_name': file_content.pop(0), 'sarah_dob': file_content.pop(0), 'sarah_time': file_content}
            fastest_time = str(sorted(set([sanitize(t) for t in sarah_data['sarah_time']]))[0:3])
            return sarah_data,fastest_time
        except IOError as ioerr:
            print('File error: '+str(ioerr))
            return(None)
    
    sarah = data_control('D:\pythondata\hfpy_ch6_data\sarah2.txt')
    print(sarah[0]['sarah_name']+"'s fastest time are:"+sarah[1])

    划重点:
    1.列表推导:用很少的代码量来完成列表的转换。

    clean_mikey=[]
    for each_t in mikey:
        clean_mikey.append(sanitize(each_t))

    转换为

    clean_mikey=[sanitize(each_t) for each_t in mikey]

    2.用集合删除重复项
    python提供了集合数据结构,集合最突出的特性是集合中的数据项是无序的,而且不允许重复。创建空集合时可以使用set()BIF,如果想一步完成集合的创建和填充,可以指定一个现有列表作为set()BIF的参数。
    3.sarah_data = {‘sarah_name’: file_content.pop(0), ‘sarah_dob’: file_content.pop(0), ‘sarah_time’: file_content}这一句代码使用到了字典。在python中,除了之前我们提到过的列表和集合以外,python还提供了字典,字典是一个python内置的数据结构,允许将数据与键而不是与数字关联。这样可以使内存中的数据与实际数据的结构保持一致。

    第六部分定制数据对象

    当python的内置数据结构无法胜任时,Python class语句还允许你定义自己的数据结构。Python遵循标准的面向对象编程模型,提供了一种方法允许将代码及其处理的数据定义为一个类。一旦有了类定义,就可以用它来创建(或实例化)数据对象,它会继承类的属性。在面向对象世界里,你的代码通常称为类的方法,而数据通常称为类的属性。实例化的数据对象通常称为实例。
    示例:文件去http://python.itcarlow.ie/resources.html下载Chapter 6 updated TXT files 。编写代码定义Athlete类,除了init()方法外,还要定义一个新方法top3(),调用这个方法会返回最快的3个时间。同时调整get_coach_data()函数,返回一个Athlete对象而不是字典。

    class Athlete:
        def __init__(self,a_name,a_dob,a_times):
            self.name = a_name
            self.dob = a_dob
            self.times= a_times
        def top3(self):
            return (sorted(set([sanitize(t) for t in self.times]))[0:3])
    
    def sanitize(time_string):
        if '-' in time_string:
            splitter = '-'
        elif ':' in time_string:
            splitter = ':'
        else:
            return  time_string
        (mins, secs) = time_string.split(splitter)
        return mins+'.'+secs
    
    def get_coach_data(file_path):
        try:
            with open(file_path) as read_file:
                data = read_file.readline()
                templ = data.strip().split(',')
            return (Athlete(templ.pop(0),templ.pop(0),templ))
        except IOError as ioerr:
            print('File error: '+str(ioerr))
            return(None)
    james = get_coach_data('D:\pythondata\hfpy_ch6_data\james2.txt')
    print(james.name+"'s fastest times are:"+str(james.top3()))

    划重点:
    Python使用class创建对象。每个定义的类都有一个特殊的方法,名为init(),可以通过这个方法控制如何初始化对象。
    类中的方法与函数的定义很类似,也就是说,同样使用def来定义。基本形式如下:

    class Athlete:
        def __init__(self):
            #The code to initialize a "Athlete" object.#

    “init”前面和后面分别有两个下划线。
    每个方法的第一个参数都是self,使用self标识调用对象实例。

    事实上,Athlete类与Python列表的区别只是Athlete类包含name和dob对象属性。Python的class允许你从零开始创建一个定制类,就像创建Athlete类那样。不过,class还允许通过继承现有的其他类来创建一个类,这也包括用list、set和dict提供的Python内置数据结构类。
    示例:重写Athlete类,让它继承内置的list类,将这个新类命名为AthleteList。

    # 提供一个类名list,新类将派生这个类。
    class AthleteList(list):
        def __init__(self,a_name=None,a_dob=None,a_times=[]):
        # 初始化所派生的类,然后把参数赋值属性。
            list.__init__([])
            self.name = a_name
            self.dob = a_dob
            self.extend(a_times)
    
        def top3(self):
            return (sorted(set([sanitize(t) for t in self]))[0:3])
    
    def sanitize(time_string):
        if '-' in time_string:
            splitter = '-'
        elif ':' in time_string:
            splitter = ':'
        else:
            return  time_string
        (mins, secs) = time_string.split(splitter)
        return mins+'.'+secs
    
    def get_coach_data(file_path):
        try:
            with open(file_path) as read_file:
                data = read_file.readline()
                templ = data.strip().split(',')
            return (AthleteList(templ.pop(0),templ.pop(0),templ))
        except IOError as ioerr:
            print('File error: '+str(ioerr))
            return(None)
    
    
    james = get_coach_data('D:\pythondata\hfpy_ch6_data\james2.txt')
    print(james.name+"'s fastest times are:"+str(james.top3()))

    划重点:
    1.“字典”,这是一个内置的数据结构,允许将数据值与键关联。
    2.“self”,这是一个方法参数,总是指向当前对象实例。
    3.使用dict()工厂函数或使用{}可以创建一个空字典。
    4.要访问一个名为person的字典中与键Name关联的值,可以使用我们熟悉的中括号记法:person[‘Name’]
    5.类方法(代码)与函数的定义基本相同,也就是说,要用def关键字定义。

    展开全文
  • Three Most Important Habits to Live a Truly Successful Life What is success?There were times when I thought success was about creating great work; times when I believed success was about making more m...

    Three Most Important Habits to Live a Truly Successful Life

    What is success?

    There were times when I thought success was about creating great work; times when I believed success was about making more money; and times when I thought success was about living in the present.

    None of the above definitions of success are wrong, but almost every single one of them is incomplete. How you define success defines you. Those definitions described part of my identity at the moment, but not me.

    Now, I usually describe success as the combination of accomplishments and happiness. For me, success is about having fun and joy along the journey of creating the results—art, reputation, lifestyle—you desire. Success is the process of growth.

    Many habits help one achieve success. I’ve written a lot about them in the past too:

    Out of all the specific tips, tricks, and habits, there are only three habits that really matter when we’re looking at success from a broader angle. These are what keep us on track, what help us persist, and what bring us pure joy.

    Practicing Self-Awareness

    Self-awareness is the first step to making your life easier in both accomplishing your dreams and experiencing happiness. In other words, it’s about knowing who you are and where you want to go.

    Practicing self-awareness harnesses your ability to make the right choices for yourself. It helps you to keep your priorities straight and direct your energy to the right thing.

    You can do anything but not everything. —David Allen

    However, self-awareness is not something that we’re born with. Just like passion, you can’t find it without testing things out.

    • Take action to test out what you love, so you don’t waste 20 years on something only to find out it’s too late to change course.
    • Figure out your strengths and weaknesses, so you don’t fight a battle that you can’t win.
    • Define who you are and build your self-identity, so you’re not trying to be everything and end up becoming nothing.

    The best way to learn about yourself is to keep asking yourself tough questions and answering them. Questions that challenge your existing paradigms and require you to think deeply. Do this every day, write them down, and reflect upon them regularly.

    (I found that most creative work is a process of self-discovery. Over the past year, writing has helped me discover and uncover a lot about myself.)

    Adopting The Child-Like Mindset

    I have a two-year-old nephew. It’s great to have a kid at home. There is a saying that kids are here to remind adults about how to live, and that’s very true.

    I found that everyone deserves to be successful because everyone is born with the traits required to accomplish great things and be happy.

    First, kids don’t overthink stuff and always see the world as it is. We don’t see kids dwell on how hard it is to build a LEGO castle. We don’t see kids overthinking about the meaning of playing. And we definitely don’t see kids hide their feelings and emotions because of their egos.

    We can act faster if we stop dwelling, live happier if we stop over-thinking, and communicate so much more effective when we speak our minds.

    The second trait I learned from my nephew is even more important: he doesn’t stop playing. What do kids do when they fall down? They get back up quickly and start playing again. And that’s exactly what many of us need to accomplish in order to get what we want in life.

    Almost all top performers and high achievers have this. They may be gifted with a better head start and opportunity, but what really makes them stand out is their ability to keep trying—again and again—every time after they’ve failed.

    Finding Fulfillment in Hard Work, Challenges, and Setbacks

    There is a group of people who complain about anything—the weather is terrible, the teachers suck, the government is dumb—and do nothing. They can only enjoy life when everything is going well.

    The truth is, life never goes as smoothly as we want it to, and often, greatness is found and nurtured in the face of adversity. Hoping to get lucky is like hoping to hit the bullseye without aiming.

    Don’t wish it was easier wish you were better. Don’t wish for less problems wish for more skills. Don’t wish for less challenge wish for more wisdom. —Jim Rohn

    Instead of hoping that life gets easier, and hoping that you will get better:

    • If you’re an entrepreneur, don’t wait for customers to come knocking at your door. Go out, share your product, and get customers for your business.
    • If you’re a writer, don’t hope for more readers to come naturally. Write every day, at the same time, reach out to other publications and writers to promote your work.
    • If you’re a music producer, don’t assume people will love what you make. Give your work away for free and build your audience slowly.

    Acknowledge that success is not easy. If it were easy, everyone would have done it. Then, be the other group—the group who finds joy in challenges and setbacks, the group who loves the things they do, and the group who puts in the hard work and gets addicted to the process.

    Accomplish Success with Appreciation

    People can accomplish a lot but still feel miserable. At the same time, some people live in the illusion that happiness is everything they ever needed without getting anything done.

    Again, I see success as a combination of both accomplishment and happiness.

    Ultimately, your habits are what lead you to them. The secret is simply to be self-aware, to keep a child-like mindset and momentum, and to appreciate and find fulfillment from every moment of your life.

    FOOTNOTES

    1. Speaking of happiness, I want to expand more on the concept of appreciation. I think it’s wrong or ineffective to seek happiness. We hustle to achieve our dreams, to make more money, to do great work. But we can never hustle to achieve happiness.
    2. What I learned about seeking happiness (and peace of mind) is that the more I seek it, the less I get it. Simply because it’s not something we can get. It’s something we choose to live with. How? Appreciate the people, things, and moments you have.

    文章内容来源:
    https://deanyeong.com/3-important-habits-to-success/


    以下为翻译内容转载自煎蛋网:
    http://jandan.net/2019/03/06/habits-to-success.html

    最重要的的三个习惯:帮你过上真正成功的人生

    什么是成功?

    我曾以为,成功就是创造伟大的作品,就是赚更多钱,就是活在当下。

    殊不知这些成功的定义都没有错,可是几乎每一个都不完整。如何定义成功,成功就会如何定义你。这些定义都只是描述出了我当前特质的一部分,但不是我的全部。

    现在,我经常把成功定义为成就和幸福的结合。对于我而言,成功就是在创造你所期望的结果的旅程中,收获欢乐和喜悦,而无论你追求的是艺术、声誉,还是某种生活方式。成功其实是个成长的过程。

    很多习惯帮助人活得成功。比如:

    - 如何清晰地思考并作出更好的决策。
    - 如何激励你自己和处理挫折。
    - 如何吃得更好以改善你的健康。

    当我们从更广阔的角度看待成功时,在所有具体的提醒、技巧和习惯中,只有3种习惯至关重要。正是这些习惯让我们走上正轨,帮助我们坚持,并带给我们纯粹的快乐。

    练习自我认知

    自我认知是让你的生活更轻松、实现梦想和收获幸福感的第一步。另一方面,它关于认识你是谁,以及你想去哪里。

    练习自我认知有助于你为自己做出正确的选择。它帮助你保持主次分明,将精力投入到正确的事情上。

    You can do anything but not everything. ——David Allen
    你能做任何事,但不是所有事。——大卫·艾伦

    然而,自我认知并不是与生俱来。就像激情,如果没有不断尝试各种各样的事情,也就不会发现自己会为什么而激动万分。

    - 采取行动去尝试,找到你热爱的东西,这样你就不会浪费20年的时间,才发现改变方向为时已晚。
    - 弄清楚你的优势和劣势,这样你就不会打一场注定无法获胜的战争。
    - 确定你是一个怎样的人,并建立自我认同,这样你就不必什么都是、什么都做,到头来却什么都不是。

    了解自己最好的方法,就是不断问自己艰难的问题,并回答这些问题。回答这些对你现有的模式提出挑战,并要求你深入思考的问题。每天如此,写下来,并定期反思。

    (我发现大多数创造性工作都是一个自我发现的过程。在过去的一年,写作帮助我发现和揭露了很多关于自己的地方。)

    养成赤子之心

    我有一个2岁的外甥,家中有个孩子真好。有一种说法是,孩子们来到这个世界,就是为了告诉大人们怎么活,这话说得很对。

    我发现每个人都应该成功,因为每个人天生就具备完成伟大事业和快乐的特质。

    首先,孩子们不会过度思考,往往能看到世界本来的样子。我们不会看到孩子老想着搭建一个乐高积木城堡有多么难。我们不会看到孩子们过度思考玩耍的意义。并且很明显,我们不会看到孩子们为了自尊,隐藏他们的感受和情绪。

    如果我们停止纠结,我们能更快采取行动。如果我们停止过度思考,我们能活得更快乐。当我们说出我们的想法时,我们的沟通会有效得多。

    我从我的外甥学习到的第二个特质甚至更重要:他不会停止玩耍。当孩子们跌倒时,他们会怎么做?他们迅速站起来,再次投入玩耍。为了获得我们想要的生活,这正是我们很多人需要达到的境界。

    几乎所有顶级的演员和成功者都拥有这样的特质。他们也许被上天赐予了更高的起点和更好的机会,但真正让他们脱颖而出的是他们每次失败后,仍然坚持不断尝试。

    在努力工作、挑战和挫折中取得成就

    有这样一群人,他们抱怨任何事情——天气多么不好、老师多么糟糕、政府多么愚蠢——以至于什么都不做。他们只有在一切都顺利的时候,才能享受生活。

    真相是,生活永远都不像我们想象中顺利,我们往往会在逆境中发现并培育伟大。寄希望于幸运,无异于不瞄准就想正中红心。

    Don’t wish it was easier wish you were better. Don’t wish for less problems wish for more skills. Don’t wish for less challenge wish for more wisdom. —Jim Rohn
    不要期望事情变得容易,而是期望自己变得更好。不要期望存在更少的问题,而是期望获得更多的技能。不要期望面对更少的挑战,而是期望领悟更多的智慧。——吉姆‧罗恩

    与其盼望生活变得容易,不如盼望你变得更好:

    - 如果你是一个企业家,不要等待顾客主动敲你的门。走出去,分享你的产品,为你的公司创造顾客。
    - 如果你是一个作者,不要盼望读者会自然而然多起来。坚持每天写作,同时,通过接触其他刊物和作家,提升你的水平。
    - 如果你是一个音乐制作人,不要假设人们会爱上你的作品。免费提供你的作品,慢慢建立你的听众群体。

    成功的确并不容易。如果成功很简单,那么每个人都能做到。所以,成为另一种人,一种能在挑战和挫折中找到快乐的人,一种热爱他们所做的事情的人,一种努力工作并沉迷于这个过程的人。

    通过欣赏来获得成功

    人们可以做很多事情,但仍然感到悲惨。与此同时,一些人活在幻觉中,以为幸福就是他们需要的一切的,而不需要做任何事情。

    再次重申,我认为成功是成就和幸福的结合。

    最终,你的习惯会引导你。秘诀很简单,就在于自我认知,在于保持孩子般的心态和动力,也在于欣赏生活中的每一刻,并从中找到满足感。

    本文译自 deanyeong,由译者 CampZhe 基于创作共用协议(BY-NC)发布。


    展开全文
  • In this article we put these frameworks head-to-head, comparing each on its merits. We rate the two on critical aspects of their design, development and runtime environments. The intention is to ...
    August 2005 
    

    Discussion


    After several years as the leading Java web application framework, the reign of Apache Struts appears to be drawing to an end. Indeed, the action-based model on which Struts is based is no longer regarded by many as ideal for Java web application development. While Struts and many other Model View Controller (MVC) frameworks from the early 2000s are largely operation-centric and stateless, the frameworks emerging most strongly are component-based and event-driven. The leading contenders in this space are the new "standard", JavaServer Faces (JSF), and Strut's cousin from the Apache Jakarta project, Tapestry.

    In this article we put these frameworks head-to-head, comparing each on its merits. We rate the two on critical aspects of their design, development and runtime environments. The intention is to provide users with a basis for making informed choices about the advantages and disadvantages of each, and for deciding which to choose when embarking on a new project. Our comparisons are based on JSF 1.1 and Tapestry 3.0.3 (with occasional references to the forthcoming Tapestry 4.0 where appropriate).

     

    Introducing the Frameworks

    JSF is the new standard web application framework emerging from JSR 127 of Sun's Java Community Process (JCP). An influential figure in JSF's creation was Struts lead developer, Craig McClanahan. Leading JSF implementations include Sun's reference implementation (JSF RI), and MyFaces, which recently became an Apache project. Many leading software vendors, including Sun, Oracle and IBM, are providing design tools targeting JSF. The current version of the specification, 1.1, was issued in May 2004. JSF 1.2, currently in public review, will also be part of the forthcoming J2EE 5.0 specification. An early access version of JSF 1.2is also available from Sun's glassfish project.

    While Tapestry is gaining momentum, it is not new. It began as a SourceForge project in 2000, inspired originally by Apple WebObjects, and joined Apache in 2003. Unlike JSF, Tapestry is not a specification; there is one implementation, currently at version 3.0.3. After a year and a half wait, the release of Tapestry 4.0 is imminent, is currently in beta.

    JSF and Tapestry have fundamental similarities that distinguish them from standard MVC frameworks:

    • they work at a higher level of abstraction, shielding the developer from having to work directly with the Servlet API

    • they bind web display controls directly to Java object properties, which may be Strings, numeric, date and other types, defined in classes which maintain state. User interactions (for example through button clicks and links) are mapped directly to Java event handling methods in these classes

    • both JSF and Tapestry support a component-based approach to web application development, where chunks of functionality can be packaged up and reused in different contexts. Each of the frameworks ships with a set of core components, enabling the most commonly required functionality

    As we'll see in the next sections, the frameworks differ greatly at an implementation level. Writing JSF applications is a very different experience to developing Tapestry applications, for most programmers.

    Sidebar: The Example Application

    I have created a simple example application which is the source of most of the code snippets appearing in this article. The application provides functionality for viewing and editing holidays booked by an individual, and includes:

    • a home screen listing listing holidays booked, including start date, number of days, and some descriptive information
    • a screen providing a more detailed view of any individual holiday booking
    • a screen to add new holiday booking entries

    The screen flow for the application is shown below:

    Example app screen flow

    You can download both the JSF version and the Tapestry version of the application.

     

    Criterion 1: Page Development

    A web application is at its heart a set of dynamic dynamic templates connected to functionality encapsulated in Java code. To the casual observer, the most obvious difference between the frameworks will appear to be that JSF templates are based on JSP, while Tapestry's templates are essentially HTML based.

    JSF

    JSF uses JSPs as its primary display technology. Standard compliant JSF implementations must implement a set of proscribed JSP tags to represent the core components. The JSP tags bear less than a passing resemblance to HTML. Here's an example of an HTML form using JSF component tags:

    <h:form>      
      <h:panelGrid columns="2" border = "1">
            <h:outputText styleClass = "label" value="No"/>
    
            <h:outputText value="#{holidaySession.currentHolidayBooking.holidayID}"/>
            <h:outputText styleClass = "label" value="Date"/>
            <h:outputText value="#{holidaySession.currentHolidayBooking.date}"/>
    
            <h:outputText styleClass = "label" value="Number of days"/>
            <h:outputText value="#{holidaySession.currentHolidayBooking.amount.value}"/>
            <h:outputText styleClass = "label" value="Description"/>
    
            <h:outputText value="#{holidaySession.currentHolidayBooking.description}"/>
     </h:panelGrid>
     <BR>
     <h:commandButton value="Back" action="#{holidaydetail_backing.home}" immediate = "true"/>
    
    </h:form>
      
    JSF pages can't be previewed using a standard web browser. To preview a JSF page, you either need to use a JSF design tool, or to deploy the application for real.

    JSP is the primary view technology for JSF, but not the only one. An interesting alternative is described in Hans Bergsten's article, Improving JSF by Dumping JSP. Bergsten's article also describes some serious anomalies arising from mixing JSF and JSP tags. These are being fixed through a combination of specification changes in JSF 1.2 and enhancements to the JSP 2.1 expression language.


    Tapestry

    For most applications, Tapestry templates are simply regular HTML, decorated with Tapestry attributes. Here's an example:

    <span jwcid = "@Conditional" condition = "ognl:currentHolidayBooking">  
            <p><strong>Holiday Details</strong></p>
            <table>
            <tr>
                    <td class = "label">No</td>
                    <td><span jwcid = "@Insert" 
                            value = "ognl:currentHolidayBooking.holidayID">1</span>
                    </td>
            </tr>
            <tr>                      
                <td class = "label">Start date</td>
                    <td><span jwcid = "@Insert" 
                            value = "ognl:currentHolidayBooking.date" format = "ognl:dateFormat">1</span>
                    </td>
            </tr>
            <tr>                               
                <td class = "label">Number of days</td>
                    <td><span jwcid = "@Insert" 
                            value = "ognl:currentHolidayBooking.amount.value">1</span>
                    </td>
            </tr> 
            <tr>               
                <td class = "label">Description</td>    
                    <td><span jwcid = "@Insert" 
                            value = "ognl:currentHolidayBooking.description">1</span>
                    </td>
            </tr>
            </table>
    </span>

     

    The jwcid = "@componentName" attribute identifies the tag as a Tapestry tag.

    Tapestry templates don't need to be HTML. Tapestry supports any markup, provided that the Tapestry tags themselves are well-formed XML, in the sense that the opening tag must be well-formed and have a corresponding well-formed closing tag. Tapestry templates are implemented in the markup language of their target environment, which would typically be HTML or WML for wireless applications. Tapestry templates can be previewed without being deployed onto a Servlet container.

    The fact that JSF tags don't look like HTML makes it harder to learn to use them initially. Not that many Java programmers I know enjoy editing HTML, but at least they are generally comfortable doing it.

    The purported benefit of the JSF abstraction is greater flexibility in targeting different devices with the same template markup. The cost, however, is a loss of control; your template no longer precisely expresses the output. There is also more to learn, as you need to learn a new tag library, and how this maps to HTML. Over time, the compactness of the JSF format may help you to edit your templates more quickly, although this advantage will be less relevant for users of JSF design tools.

    For me the costs of the JSF approach outweigh the advantages. The same template will rarely be used for different devices except in the simplest cases. Although JSF design tools make constructing and previewing JSF templates easier, they are no direct substitute for the mature HTML design tools currently used by web designers. The JSF approach also places more reliance on Java developers for layout design, because most HTML page designers will be uncomfortable using JSF tools.

    JSF users looking for an alternative to JSP have grounds for confidence that one will emerge. JSF employs an extensible architecture which allows major portions of the framework to be extended or replaced when necessary. One of the extension points is the view handler. JSF allows alternative view handlers to be plugged in using practically any display technology. A very promising view handler implementation is Facelets, a recently created java.net open source project. Facelets draws its inspiration from the Tapestry templating model, with the aim of allowing full integration with JSF without the burden of JSP. Among some advanced templating features, Facelets allows you to create Tapestry style tags such as <input id="bar" type="text" jsfc="h:inputText" value="#{foo.bar}"/>

    It will be interesting to see in the coming period to what extent Facelets is adopted by developers, and influences future revisions of the JSF specification.

    Tapestry's ability to blend into a standard HTML markup is its distinguishing and most valuable feature.

    Criterion 2: Java Programming Model

    We've mentioned that both Tapestry and JSF allow web templates to interact directly with methods and properties defined in Java classes. The form these classes take, and how instances of these classes are created and managed at runtime, is governed by the framework's programming model.

    Tapestry

    Each page screen in your Tapestry application typically consists of a page template (usually HTML), a Java class with properties and methods, and a page specification file used to define how template controls are bound to Java classes. There are variations around this. Tapestry has a specific mechanism for accessing HttpSession- or ServletContext-bound state; it is possible to do so directly from within a template or page specification.

    Also it is possible to have a skeletal page specification with all data binding defined in the template itself. It is not possible, however, to bind display controls to instances of arbitrary classes with request scope. Request scope bindings must be to properties and methods accessible through a specific page class. The main restriction regarding the page class implementation is that it needs to subclass BasePage or AbstractPage. In other words, the classes in which you write your presentation logic inherit a lot of dependencies which ideally should be internal to the framework. Future versions of Tapestry should remove these restrictions and allow for a less coupled programming model.


    JSF

    In JSF there is no page specification file. Instead, there is a global configuration file, called faces-config.xml, in which as set of "managed beans" are defined. Managed beans are regular JavaBeans with properties and event listeners. Each backing bean is identified in faces-config.xml by three items of information: a String identifier, a Java class name, and a scope for the bean, which may be request, session or application. Once defined in faces-config.xml, a display control on any JSF template can reference the backing bean using the identifier. Managed beans can be configured to reference other managed beans.

    Both JSF and Tapestry integrate easily with other middle tier technologies such as Spring. The JSF managed bean facility is an Inversion of Control (IoC) container in its own right. Through extensions such as the JSF-Spring, it is possible to integrate extremely closely with Spring, allowing for instance Spring bean methods to be called using JSF expressions. While Spring integration is fairly straightforward with Tapestry 3.0, its IoC capabilities are greatly enhanced in Tapestry 4.0; Tapestry 4.0 has been rebuilt on the Hivemind, a IoC framework also started by Tapestry lead Howard Lewis Ship. Spring beans can be easily injected into Tapestry application classes.

    The JSF model offers more flexibility in where you put application code, because you are not limited to using the page class. You can also achieve code reuse through composition. For example, you can set up page-specific managed beans for functionality applying to individual pages, with these beans holding references to other managed beans containing functionality shared between pages.

    With Tapestry you need to use class inheritance to eliminate duplicate code in the same way. The fact that the application's page class must inherit from a framework specific base class (containing lots of framework-specific state) is not ideal.

     

    JSF also has a more intuitive mechanism for managing session and application scoped state: any page can connect to any managed bean, whether it has request, session or application scope. Tapestry limits the application to using persistent page properties or an application-defined Visit object (for session scope state), or to an application defined Global object (for application scoped state). Tapestry 4.0 removes this limitation with the introduction of Application State Objects, which appear to be similar to JSF managed beans. Tapestry 4.0 also provides a number of productivity enhancing refinements, including support for Java 5.0 annotations, reduced need for XML, and simpler application configuration.

    A quirk with Tapestry is the fact that page and component classes are normally abstract. They don't have to be, but in quite a few ways the framework encourages you to make them so. Most other frameworks which employ byte code enhancement (e.g. Hibernate) don't impose this rather unnatural mechanism on the programming model.

    JSF has an intuitive and flexible programming model for creating application classes that is free of the few idiosyncrasies present in Tapestry.

    Criterion 3: Request Processing Life Cycle

    At the heart of a web application framework is the request processing life cycle, the set of steps the framework completes and calls executed during the processing of an individual request. The request processing life cycle should be as efficient as possible but hooks in the right places for inserting custom request handling logic in an elegant way.

    JSF

    JSF's life cycle is implemented in a very clearly defined six step process: Restore View, Apply Request Values, Process Validations, Update Model Values, Invoke Application and Render Response. At each point after the second of the six stages - Apply Request Values - it is possible to short-circuit the process and move directly to the Render Response phase, or even to output a response directly and notify the JSF runtime that response handling is complete. Any method with access to the JSF FacesContext object, including phase listeners, event handlers, converters and validators can short-circuit the normal request handling life cycle.


    Tapestry

    While JSF consists of a single life cycle model, the Tapestry life cycle depends on the engine service being invoked. This is because each engine service has its own life cycle. For example, the Direct Service handles form submissions, while the Page Service can be used to simply render a page without any additional server side processing. Each engine service has a life cycle which is designed to suit the task it is trying to accomplish. For very specialized requirements, it is possible to create your own engine service with a customized life cycle.

    JSF's life cycle mechanism is easier to grasp conceptually. In Tapestry's favor is the fact that the life cycle is tuned to the task at hand, allowing a more elegant solution to some problems. With JSF there may be instances where it is difficult to figure out how the life cycle mechanism is actually being used, particularly when individual components which short-circuit the standard life cycle are present.

    JSF is more accessible, while the Tapestry approach is well suited to more sophisticated requirements in that allows one to refine the life cycle to the requirements of an individual operation.

    Criterion 4: Navigation

    In a typical web application, we're interested in two types of navigation: action-based navigation, usually resulting from submitting a form via HTTP POST, and simple page navigation, for example when clicking between links through read-only screens. With Tapestry and in particular JSF, the distinction between these is blurred somewhat. This is because both frameworks make extensive use of a postback mechanism, in which the page that renders a form or view also handles user interactions with the rendered screen. Notwithstanding this similarity, JSF and Tapestry use quite different mechanisms for moving between pages in the application.

    Tapestry

    When a user interacts with a Tapestry view, for example by clicking a link or submitting a form, a listener method on the server is invoked. The listener method contains logic to determine whether to navigate to a different page, and which page to navigate to. Navigation logic is defined in code, rather than in a configuration file. Navigating between pages is simply a case of activating the required page, using the IRequestCycle.activate() method. The example below shows how this is done.

    Lets consider how it works with our example holiday booking application.

    Tapestry example application home

    The home page contains the following code

    <input jwcid = "new@Submit" listener = "ognl:listeners.newSubmit" name="new_submit" type="submit" value="New" />
    to bind the submit button to the newSubmit() method in the page class.

     

    The newSubmit() method in the page class handles the transfer by calling the IRequestCycle's activate() method.

    public void newSubmit(IRequestCycle cycle)
    {
            cycle.activate("NewHoliday");
    }

     

    This mechanism can be used for simple page navigation (using the DirectLink component) as well as form submission. Tapestry also provides alternatives which do not involve posting back to the current form. The ExternalLink component can be used to navigate directly to another page in the application. Here, the target page needs to implement the IExternalPage interface.

    Application-specific parameter values can be encoded into the URL, and received by the target page as an array of Objects (rather than String name value pairs). But this means that your activateExternalPage() implementation needs to read the encoded parameters in the correct order.

    As long as the target page does not rely on HttpSession state, the ExternalLink URL can be used to bookmark pages. But the format is far from user friendly: here's an example: http://localhost:8080/tapestry/app?service=external/HolidayDetails&sp=4. Tapestry 4.0 will allow for much more user-friendly URLs, at the price of extra configuration effort.

    Tapestry supports redirects in a rather clumsy way by allowing you to throw a RedirectException. It requires some effort to implement the Redirect After Post pattern in Tapestry. One approach is to use a "redirect" page which in turn passes parameters to a target page using the ExternalLink mechanism.


    JSF

    In JSF, you need to use a combination of code and configuration to transfer control to a different page after posting a form. Here's what's required:

    • your form control (HtmlCommandButton or HtmlCommandLink) must be bound to a managed bean action event handler. The event handler must have the form

      public String actionName();

      You may initially get confused between action event handlers and action listeners. The latter has the signature

      public void actionListenerName(FacesContext context);

      and cannot effect navigation

    • the String value returned from the action method must correspond to an entry in the faces-config.xml, which maps this value to a subsequent view. Here's an example from the JSF version of our holiday booking application:

      <navigation-rule>
              <from-view-id>/home.jsp</from-view-id>
              <navigation-case>
                      <from-outcome>newHoliday</from-outcome>
                      <to-view-id>/newholiday.jsp</to-view-id>
              </navigation-case>
              <navigation-case>
                      <from-outcome>detail</from-outcome>
                      <to-view-id>/holidaydetail.jsp</to-view-id>
              </navigation-case>
              <navigation-case>
                      <from-outcome>delete</from-outcome>
                      <to-view-id>/home.jsp</to-view-id>
              </navigation-case>
      </navigation-rule>

      You can specify navigation as a redirect rather than forward.

      Struts users will no doubt recognize the navigation-rule mechanism. Because these rules are centrally located in an XML document, it's fairly straightforward to visually represent and manipulate these using a tool.

    • As a shortcut, you can also put the navigation action straight into the page, as in <h:commandButton value="next" action="next" />

    With JSF you also have an alternative mechanism for simple page links within the application. The HtmlOutputLink can be used instead of the HtmlCommandLink. Parameters are encoded as HttpServletRequest parameters. They can be retrieved from the target page as Strings via the FacesContext object. In this case, you still need to handle the String to object conversions yourself, something which you can avoid when working with Tapestry's ExternalLink.

    In my view, it's a shame JSF retained Struts's mechanism for navigation. Navigation should be regarded as part of the application logic, not configuration. Defining all navigation rules in a single configuration file removed from the logic which determines this navigation is an abstraction which in my opinion serves little purpose.

    JSF could also make use of a counterpart to Tapestry's ExternalLink. While the IExternalLink mechanism is not perfect, it at least allows you to preserve parameter type information, and the IExternalPage interface provides a well defined mechanism for extracting request parameters for pages loaded in this way, something which JSF lacks.

    Both frameworks have quirks; my preference is for Tapestry's code-based approach. to navigation.

    Criterion 5: Event Handling

    Both Tapestry and JSF use event handling mechanisms to allow the application to respond to user interactions and changes in the request processing life cycle. Event listeners are the entry points into your application's functionality.

    JSF

    JSF event model was designed to be similar to the JavaBeans event model. The main JSF event handlers are the action methods and the action listeners, both introduced in the previous section. Note that it is possible and sometimes necessary to define both an action listener and an action method for a component instance. This is because action listeners cannot influence navigation, while action events have no access to information on the component generating the event. Some users may find this distinction a bit confusing or even contrived.

    JSF also has value change events, which allow for automatic resubmission of forms when input values are modified. There is no equivalent in the current version of Tapestry.

    In addition, each phase in the request handling life cycle triggers a phase event both before and after the phase occurs. Phase listeners can configured in faces-config.xml to capture and respond to phase events. The phase listeners are not connected with any particular view or page. If this information is needed, it must be retrieved from FacesContext, a slightly laborious task.

     


    Tapestry

    In the previous section we saw an example of the Tapestry workhorse event handler, which for Tapestry 3.0 are page class methods with the signature

    public void methodName(IRequestCycle cycle);
      
    This event handler is used both for interfacing with the service layer and for navigation. Tapestry 4.0 offers more flexibility in how the listener method is defined, for example in allowing you to define method parameters by name. The snippet <a jwcid="@DirectLink" listener="listener:handleClick" parameters="ognl:{ id, value }"> ... </a> would be used to invoke the method in the page class: public void handleClick(int id, String value) { ... }.

     

    An advantage of Tapestry page classes is that event handling can be tied specifically to different stages of the page loading and rendering process. Methods your page class can implement include:

    • public void pageBeginRender(PageEvent event): allows page state to be configured before rendering occurs, for example, by reading data from a database
    • public void pageValidate(PageEvent event): allows the page to be validated, for example, by checking that the current user is authenticated
    • public void pageDetached(PageEvent event): allows for release of resources used during the request

     

    Both tapestry and JSF have event models with hooks to which application functionality can be attached. Tapestry and JSF differ markedly in the way they handle request life cycle events. Having a page specific event model allows Tapestry to shine for events which need to be handled in a page-specific way, since it provides natural places for this functionality to be added. By contrast, the JSF phase listener approach is fairly ungainly for this type of processing.

    On the other hand, phase listeners work better for life cycle handling functionality which needs to be shared across the application. With Tapestry, reuse of event handling logic would need to occur through a common superclass.

    JSF benefits from a value change listener events. The life cycle event model works better for Tapestry when event handling logic is page-specific, and favors JSF for application-specific life cycle event handling

    Criterion 6: Component State Management

    A JSF or Tapestry template needs to be transformed into a component tree before it can be used. This applies in particular for rendering forms and handling form submissions. One of the fundamental differences between JSF and Tapestry is how in how they manage component state.

    Tapestry

    Tapestry aims for an efficient component state management mechanism by minimizing the number of times a page's component tree is loaded. When a page is requested for the first time, an instance of the page class, together with instances of all its contained components, is created. When the request has been serviced, the page instance is added to an object pool. Subsequent requests can reuse this page instance, returning it to the pool when finished. New instances of the same page will only be created if no page instance is available in the pool when the request arrives.

    What makes this possible is that a Tapestry component tree is static. It cannot be modified programmatically at runtime. Whether a component displays (conditional evaluation) and how many times it displays (iteration) depend entirely on data supplied to a component. For example, the number of items displayed by a Foreach component depends on the size of the Collection passed in as an input to the component.

    The downside of Tapestry's data-centric approach is that it introduces a few subtle issues into the way that form submission is handled. Tapestry has no equivalent of the Restore View phase. To recover values obtained from form submission, it re-renders the same form, populating component properties and applying data bindings in the process. However, if the model data used to re-render the form changes, the rewound form is no longer in sync with the submitted data, resulting in a StaleLinkException. This issue is probably the number one pitfall for novice Tapestry users, and surely the number one cause for complaint on the Tapestry users' mailing list. Tapestry 4.0, however, provides at least a partial solution to the problem in the form of the For and If components, which store data used for rendering in hidden fields, allowing this same data to be used to rewind the form during postback.


    JSF

    JSF does not specifiy exactly how component state should be managed, leaving this as an implementation detail for the view handler. The default view handlers of both MyFaces and JSF RI, however, work in a similar way. When a page is rendered, the component tree for the view being rendered is built on the fly. The application can be configured to use client or server-based component state management. If server based state management is used, the view is saved in user's session as a serialized object. This happens when the View component's JSP tag handler is executed.

    When data is posted, the same set of components need to be reconstructed as were originally rendered. This is to allow the components' state to be updated with the user-supplied values, which in turn need to be converted to the correct types and validated. The component tree is reconstructed during the Restore View phase, either from serialized HttpSession data, or from a serialized object passed in from the client. MyFaces currently only holds the last view in the session, while by default the JSF RI will hold up to 15 serialized views in the session.

    JSF does not suffer from the problems associated with Tapestry's rewind process, because it maintains the component state from the previous render. This greatly eases the task for the developer. The problem for JSF is likely to be one of efficiency. Building a component tree is quite an expensive task, so it is something which the framework should do sparingly. It's questionable whether JSF succeeds on this count.

    Firstly, the component tree will often need to be rebuilt when a different JSP is rendered; it is not cached indefinitely. Unlike Tapestry, the JSF component tree is dynamic; components can be (and sometimes have to be) instantiated and manipulated within the application. This makes implementation of a component tree pooling scheme similar to Tapestry's a much tougher task. JSF 1.2 is heading for a compromise position, encouraging a separation of component state into tree structure (component hierarchies and parent child relationship) and tree state (component attributes as well as objects attached to components). Only tree state would need to be managed on a per session basis; structure information could be shared efficiently across application users.

    Secondly, for some page requests, the Restore View phase is not necessary. If the purpose of an event interaction is simply to navigate to a new page without processing any form data, then restoring the previous view is an unnecessary drain on resources.

    Don't be surprised if Tapestry applications perform better than their current JSF equivalents because of a more efficient component state management mechanism. The price, of course, is more difficulty for the developer in ensuring that the application is free of stale links caused by the Tapestry rewind mechanism.

    Tapestry's efficient component state management mechanism inspires more confidence about its ability to perform and scale. For applications which don't require high performance, the rewind problem is probably one you would happily do without.

    Criterion 7: Standard Components

    The standard components are the well documented, out of the box components that you get for free. They are the components that you can confidently expect to be ported to future versions of the framework. Both JSF and Tapestry provide a set of standard components. By restricting yourself to using standard components, you can avoid vendor lock-in, component compatibility issues and license costs. The question is, how far can you stretch your application, how sophisticated can you make it, before having to look for alternative component sources to meet your application's needs?

    Tapestry

    Tapestry provide an impressive set of standard components, covering all the main HTML controls, links, as well as components for iteration and conditional inclusion of template content. With core components alone it's possible to create fairly sophisticated form-based applications which include file uploads, rollovers, calendar-based date selection, etc. Tapestry also provides a number of other "contributed" components which are not core framework components, but are still part of the official distribution, for more advanced or specialized application requirements.


    JSF

    The standard JSF components cover all the standard HTML controls. However, there are some fairly basic omissions. There is also no explicit iteration or conditional logic handling components. Working around the latter is fairly straightforward in that every component includes an rendered property which determines whether the component is visible.

    The absence of iteration is more problematic. One workaround is to use the HtmlDataGrid component, which allows for tabular output of dynamic data. But there are limitations in the way that HtmlDataGrid can be used with other components. For example it is not possible to dynamically create a set of radio buttons, with each radio button linked to a row on the HtmlDataGrid output. In other words, a form such as the Tapestry holiday booking home page cannot be rendered using standard JSF components. Instead, we need to use links within each row of the form, as shown below:

    JSF example application home

    It's also not currently possible to use JSF tags within JSTL iteration or conditional blocks, as explained in Kito Mann's article in Java World. JSF 1.2 and JSP 2.1 will together bring better integration between JSF and JSTL tags, for example in allowing JSF tags to be used effectively within <c:forEach> and <c:if> tags.

    Also missing from the standard set are more sophisticated components, such as the file upload and date picking components provided by Tapestry. Of course, you should have no trouble accessing these components for your JSF applications. There are already a rich array of JSF controls available from third parties, such as Oracle ADF Faces. The problem is potential incompatibility: if you use the ADF Faces components for example, you're most likely to end up replacing all of the standard components with ADF equivalents. If vendor independence is a goal, this is not desirable.

    Tapestry's out of the box components provide a clear advantage over JSF in terms of the sophistication of functionality that they support. The gap is likely to narrow, and if you are prepared to make extensive use of third party components, then this should not be too much of an issue.

    Tapestry provides a more advanced set of standard components, although JSF benefits from the range of third party components available.

    Criterion 8: Component Development

    In spite of the variety of existing components available, it probably won't be long before you find the need (or urge) to create your own components. How successful, enjoyable and productive this process is depends on the component development model of the framework you're using.

    Tapestry

    The first thing to note about Tapestry components is that pages are themselves components. If you're used to writing Tapestry page templates, then writing basic components only requires knowledge in a couple of new areas:

    • firstly, component textual output can be created either using a component (HTML) template (which is essentially no different from a a page template) or by writing markup code in a Java class. Using component templates, it is particularly easy to write composite components. If your component writes its own markup you will need to create a component class extending BaseComponent, and implement renderComponent(). Tapestry provides you with an IMarkupWriter implementation which makes writing out markup content relatively easy

    • secondly, unlike pages, components have parameters. Suppose for example you want to create a component which displays monetary values. This component takes three parameters: a float value, a Format object used to specify the format of the value, and the currency code. Here, the component can be built simply using two Insert component instances in a component template, with currencyCode, amount, and numberFormat as the three component parameters. This is what the component template HTML will look like:

           
      <span jwcid = "@Insert" 
              value = "ognl:currencyCode"/>&nbsp;
      
      <span jwcid = "@Insert" 
              value = "ognl:amount"
              format = "ognl:numberFormat"/>

      A page using the component simply includes attributes for each of the component parameters:

      <span jwcid = "@CurrencyAmount" currencyCode = "ognl:currencyCode" 
          amount = "ognl:amount" format = "ognl:decimalFormat"/>
      You don't need to write code to bind your parameters to page properties; Tapestry will do this for you in all but the most specialized situations.

      A useful feature is Tapestry's supports informal parameters, tag attributes not needed by the component itself but are still required for layout purposes. A good example is CSS class references. Informal parameters can be transparently rendered without the need for any component level declaration.


    JSF

    Compared with Tapestry, implementing a custom JSF component is a relatively demanding exercise. Unlike Tapestry, there are no similarities in the component development and page development models. Component templates are not supported, which means you need to write contained markup using Java code. JSF custom components will typically need to following:

    • an implementation of the component class, which will extend UIComponent, normally by subclassing UIComponentBase or one of its subclasses. This class may contain fields used to hold component state

    • an implementation of the JSP tag handler, which is used to set up binding between the JSP templates and component, and is usually a subclass of UIComponentTag

    • optionally, the component can use an external renderer. One of the component's main tasks is to perform encoding by generating markup and decoding it by extracting inputted values from the request. Using an external render allows these roles to be delegated, opening up the possibility of different renderers being used for the same component

    The main limitation of the JSF component model is absence of built-in support for templates. All text instead needs to be generated by ResponseWriter methods, which work in the same way as Tapestry IMarkupWriter. This can be fairly labor intensive, especially when the markup text is complex or includes JavaScript. There are quite a few complexities with JSF component development. The tag handler must explicitly deal with informal parameters, since JSP custom tag contract does not support this concept. The tag implementation and component may need a fair amount of code to maintain state for instance fields, create method and value bindings, and manage child components. Much of these operations are fairly generic and can be abstracted into utility classes or component base classes. However, this involves work upfront, unless you can find an existing library or implementation to facilitate this.

    Custom component development with Tapestry is relatively simple compared to JSF.

    Criterion 9: Validation and Conversion

    An essential feature of any web application framework is strong support for validation. Type conversion is a closely related task, in the sense that successful type conversion is often a precondition to successful validation of an inputted value. Tapestry and JSF both have built in validation frameworks, but with significant differences.

    JSF

    In JSF validation is a very generalized concept. Zero or more validators can be applied to any standard JSF input component, using tags such as the following:

     

    <h:inputText required = "true" value = "#{holiday_backing.amount}" id="amountInput">
            <f:validateDoubleRange minimum = "0.1"/>
    </h:inputText>

     

    Similarly, JSF allows also you to separately specifiy either a standard or custom converter for any input component. In this case, the validation will be applied on the inputted value after conversion is successful. Here's an example:

    <h:inputText required = "true" value = "#{holiday_backing.date}" id ="chooseDateInput">
            <f:convertDateTime pattern="yyyy-MM-dd"/>                 
    </h:inputText>
      

    JSF defines a component model for creating custom converters and validators. If validation or type conversion is unsuccessful, a component specific FacesMessage instance is is added to FacesContext. The message contains summary, detail and severity information.

    Validation can also be delegated to a managed bean by adding a method binding in the validator attribute of an input tag. This mechanism is particularly useful for accomplishing form validation, where combinations of inputted values need to be evaluated to determine whether validation should succeed.

    Outputting of form-level error messages is more problematic. The JSF core component set provide an HtmlMessages component, which simply outputs the summary message from all the FacesMessage instances added to the FacesContext during validation. In practice this is not very likely to be useful; you'll probably need to create an application specific mechanism for your project to handle this more acceptably.


    Tapestry

    Validation support is added through the IValidator interface. Unlike JSF, where validator functionality can be attached to any core input component, validation in the Tapestry 3.0 core components are limited specifically to text input fields components, namely ValidField and subclasses. For each of these, the template or page specification can be used to specify a IValidator class to be used for validation if a value is specified. If the field is required then this is set at the validator level. A fair number of IValidator implementations are provided out of the box, including validators for text, numerical fields, date fields, URLs, emails and patterns.

    The scheme works well in most circumstances but there are some limitations. Validators cannot be attached to arbitrary form input components. Validation isn't supported for some core components, such as TextArea. Also, only one IValidator instance can be applied to a validating component, which makes the task of performing multiple validations somewhat problematic.

    Where Tapestry in its current form comes through more strongly is in validation of the form as a whole. During validation, information about which fields have failed validation is stored in an IValidationDelegate instance. The IValidationDelegate can be subclassed to control how field labels are rendered for fields in error. The IValidationDelegate can also be used to output customized form specific error messages.

    Tapestry's other major strength is support for client side validation. Unlike JSF, standard validators all have JavaScript-based client-side support.

    Type conversion also works quite differently in Tapestry from JSF. For fields being validated, the validator itself is responsible for performing type conversion, a possible limitation if type conversion and validation need to be decoupled. For example, the DateValidator allows you to specify a date format for Strings to be converted to and from java.util.Date instances.

    The Tapestry developers have recognized limitations in the existing architecture; Tapestry 4.0 will ship with a completely new mechanism for validation and type conversion. It will be possible to specify validators and type converters (translators) separately. It will be possible to attach multiple validators to a single component, and provide validators for a greater variety of the standard components.

    Tapestry provides a polished and functional validation implementation, and in particular handles form-specific validations well. However, there are situations where its limitations are likely to be evident. In these cases, the workarounds are straightforward enough, typically requiring validation code in the page class.

    JSF offers a conceptually more powerful validation and conversion framework than what's available with the current version of Tapestry. However, JSF's implementation is not as advanced in many ways: it lacks support for client side validation, form-level error display, and has fewer validator implementations available out of the box. If you use JSF, you will probably need to find third party validators and converters or write your own custom implementations.

    JSF has the edge conceptually, but Tapestry compensates through client side validation support, better form error management, and more out the box validation implementations.

    Criterion 10: Internationalization

    A web application framework must provide support for internationalization to allow for web applications to be localized. Both Tapestry and JSF provide built in support for internationalization.

    JSF

    Setting up internationalization in JSF is fairly straightforward:

    • add entries in faces-config.xml indicating which locales are supported, and add corresponding localized properties files into your application's class path (under WEB-INF/classes)

    • load the resource bundle you wish to use for a particular view into a into a variable, as shown in below: <f:loadBundle basename = "ApplicationMessages" var = "bundle"/>

    • instead of using a hardcoded strings for textual output, use the <h:outputtext value = "#{bundle.messageName)"/>

    • For parameterised messages, you can use the <h:outputformat/> tag, passing extra parameters in using nested <f:param> tags

    • the locale can be detected automatically, or you can explicitly set the locale for a view by calling UIViewRoot.setLocale()


    Tapestry

    Localization support in Tapestry is equally straightforward

    • for each component or page which needs to be localized, add corresponding locale specific properties file containing application messages

    • use the key attribute to identify text which needs to be localized
      <span key="hello">Hello</span>

    • for text that needs to be formatted with parameters, use the insert tag: <span jwcid="@Insert" value="ognl:messages.format('holidays_left', noDaysLeft)"/>

    Tapestry also allows localized versions of templates files. This makes sense when templates consist of relatively large amount of text relative to formatting.

    Both Tapestry and JSF provide the necessary support to fully localize your application. Tapestry provides a more compact format, and the ability to preview even localized templates using just a web browser is a real advantage. An annoyance with Tapestry 3.0 is that message files are page or component specific, which makes it difficult to share messages which need to be used by multiple pages or components. Tapestry 4.0 will fix this.

    Tapestry's advantages are a compact format and internationalized pages which can still be previewed.

    Criterion 11: Testability

    An increasingly important requirement for web applications is that they can be easily unit tested. The testability of an application depends directly on how free your application code is from framework specific dependencies.

    JSF

    In general JSF managed beans are relatively free from framework specific dependencies. There are two exceptions: firstly, event handler methods will often need to use FacesContext to extract data required for processing. Secondly, it is possible, and sometimes necessary, to bind JSF UI controls to managed beans. For example, in the home page of our example application, this is necessary to identify the data for the selected row from the HtmlDataTable component. When these kinds of dependencies are present, mock objects will aid unit testing. Otherwise, standard unit testing techniques can be used with JSF.


    Tapestry

    Tapestry pages are harder to test than JSF managed beans for two reasons. First, your page classes must extend a Tapestry base class. Secondly, Tapestry encourages pages and their property accessors to be declared abstract, allowing them to be placed under the control of Tapestry's runtime. This has some advantages, but ease of unit testing is not one of them. The forthcoming version of Tapestry has support for instantiating these pages and components within a unit testing environment, but for the current version, you have to create your own mechanism for doing this.

    JSF managed beans are definitely easier to unit test than their Tapestry counterparts, page classes. This advantage does not really apply for JSF custom components and less still for JSF tag handlers, which have significant runtime and framework dependencies.

    For testing web UIs, web integration testing (for example through HtmlUnit or Watir) is generally more valuable than than unit tests, because the latter aren't really capable of effectively mimicking user interaction. Of course, business logic should be factored out into a separately tested service layer. Nevertheless, it is still worth writing unit tests for classes with presentation logic, and important to know that you can.

    Relative freedom from framework dependencies makes it easier in general to unit test JSF managed beans than Tapestry page classes.

    Criterion 12: Developer Productivity

    For both Tapestry and JSF, their ultimate raison d'etre has to be the proposition that they are more productive than their competitors. Both JSF and Tapestry are very productive compared to traditional MVC frameworks largely because they allow you to write applications with less code. How they measure up against each other also depends on how efficiently this code can be created and modified, and how easily and quickly errors can be detected and corrected.

    Tapestry

    Tapestry also offers a number of features that help productivity. The most important of these is the ability to design templates using any HTML editor, and to preview templates within a standard web browser without deploying to an application server. Other useful features include:

    • "line precise" error reporting, where template or specification errors are identified and
      pinpointed to exact locations within the application
    • an exception page which gives detailed error and environment information
    • the Tapestry inspector, which gives a fairly detailed view of the state of the application

    Tapestry does not rely heavily on tools. A couple of tools are available for the Eclipse IDE, which can be quite helpful. No WYSIWYG drag and drop visual design tools exist for Tapestry in the same way as for JSF.


    JSF

    JSF is by design well-suited to tool support; with a good visual tool, you should be able to assemble applications fairly quickly without having to write too much code. However, the absence of a browser-based preview mechanism makes you very reliant on the tool: your productively will depend heavily on how effectively you work with it, and how effectively it works for you.

    Available JSF implementations appear to be less helpful in identifying application configuration errors. They don't for example provide line precise error reporting on the original JSPs. This may be partly because the JSF implementation controls a narrower range of the technology stack than does Tapestry, where the templating mechanism is internal to the framework.

    The feedback provided by the Tapestry runtime is helpful in limiting the episodes of "pulling out hair" attempting to solve a particular problem. The write/deploy/test cycle appears to be a bit slower with JSF, partly because the time taken to compile JSPs generates extra overhead not present in Tapestry applications.

     

    Productivity with JSF will improve as tools become more sophisticated and better used. For developers preferring a code-centric approach, Tapestry is likely to remain more productive.

    Criterion 13: Ease of Learning

    In the same way that powerful features and the promise of greater productivity in the future will draw developers to a framework, the perception that a framework is complex or hard to learn will put many off.

    Tapestry

    Tapestry is reputed to have a fairly steep learning curve. Part of the reason is that it is very different from anything that most Java developers (with the exception of those familiar with Apple WebObjects) are used to. Some understanding of how the framework works is necessary to use it properly; simply treating it as a black box is likely to result in problems.

    Barriers to entry are arguably raised with the imminent arrival of Tapestry 4. Tapestry 4 goes well beyond simple evolution of the framework; in many ways it is a complete overhaul of the way things work internally. The result is a more powerful, productive framework. However, additional learning is required. For example, you need to acquire at least a basic understanding of Hivemind. One problem is that documentation is still playing catch-up. A gap has opened between the newly introduced changes and features, and the documentation available for them.


    JSF

    While JSF is certainly more complex than Struts, it is probably conceptually simpler to understand than Tapestry. JSF has the benefit of many articles and already several books available to help new users to get accustomed with the framework. For Tapestry users there is currently only one real choice, Tapestry In Action written by Howard Lewis Ship, but this does not cover Tapestry 4.0 developments.

    With all this material available, the new JSF developer will still need to take the trouble to learn a new set of JSP custom tags which are in many ways quite peculiar to JSF.

    JSF has some advantages over Tapestry in terms of ease of learning; in particular greater availability of tutorial material, and a slightly simpler framework. Tapestry HTML templates, however, are easy to follow, although you'll need to invest some effort learning how to configure template components and page classes.

    The JSF framework is more accessible to the average developer, although getting started with template creation is easier with Tapestry.

    Criterion 14: Industry Momentum

    While the main focus of this article is on technical considerations, choosing a web framework cannot be done in a vacuum; the industry momentum behind the framework, the strength of the developer community and the extent to which industry leaders are prepared to throw their weight behind a framework, are all aspects which need to be taken into consideration.

    Third Party Components

    Because Tapestry and JSF are component-oriented UI frameworks the availability and quality of third party components is important. JSF components are available from a wide variety of sources, from commercial vendors through to open source projects. A fairly long list is available on JSFCentral.com. The presence of third-party component sources for JSF is essential, firstly because the standard components are unlikely to meet the requirements of complex applications, and secondly because writing JSF components is not trivial. Third-party Tapestry components appear to be available almost exclusively through the open source community, and are provided in a more informal, grass roots kind of way. See the Tapestry Wiki for a list.


    Specification vs Implementation

    The success of non-standard open source frameworks such as Spring and Hibernate at the expense of EJB has reopened the debate on the value of standards in Enterprise Java. The value of standards increases for projects with large development teams, higher developer turnover, and for projects which use more junior developers. For such projects, JSF's position as a standard should appeal. Standardization also offers more protection from future API changes, as backward compatibility tends to be strongly enforced with JCP standards, and allows you to avoid being locked into a single implementation. Tapestry is of course not a standard, and I find it hard to imagine that it ever will be. It's more likely that Tapestry will influence the evolution of other standards, in particular JSF.


    Availability of Jobs and Staff

    Industry momentum is important for developers and project managers for complementary reasons; developers can find jobs and managers can find staff more easily if there is momentum behind the framework. The number of jobs requiring JSF skills is increasing, but still an order of magnitude fewer than those requiring Struts. Tapestry job postings are relatively few and far between.

    With the industry support behind it, JSF has some advantages. Its position as a standard with leading vendor support makes it easier to sell to IT managers (and other developers). In spite of its active and helpful user community, going with Tapestry will put you in more lonely territory than going with JSF.

    JSF's position as a standard with broad industry support will make it more attractive to many IT managers and developers.

    Criterion 15: Extensibility

    The extensibility of a framework is how easily you can replace or add to the parts of the framework that you don't meet your needs, without affecting other parts of the framework.

    JSF

    JSF was designed from the outset to be extensible. It has a number of well-defined extension points, put to use by a growing number of third party framework add-ons. Not least of these is Struts Shale, currently being built using a new and completely different architecture and code base from Struts as most developers know it. The main extension points of JSF are:

    • view handler: this part of the framework is responsible for handling the Render Response and Restore View phases of the JSF life cycle. An alternative ViewHandler implementation can be used for plugging in different display technologies, as in the case with Facelets and Shale's Clay plug-in
    • state manager: responsible for saving and restoring a view's component tree. The StateManager is actually used by the view handler for both operations, but this use is fully decoupled. The state manager mechanism allows for pluggable component state management strategies
    • navigation handler: responsible for selecting the next view to display based on the logical outcome of application actions. The behaviour of the default NavigationHandler is described in the 'Navigation' section of this article
    • expression language: custom PropertyResolver and VariableResolver implementations can be supplied to extend the JSF EL, for example, by adding new implicit variables. For JSF 1.2, expression language extensibility will be through JSP EL mechanisms
    • application and application factory: the Application and ApplicationFactory implementations can be customized to control how references to pluggable JSF objects are provided.

    Applying customizations of each of these parts of JSF is simple; just register the appropriate entry in faces-config.xml. For example, configuring your JSF application to use the Facelets view handler requires the entry:

    <application>
            <view-handler>
                    com.sun.facelets.FaceletViewHandler
            </view-handler>    
    </application>

     

     


    Tapestry

    The main framework extension point for Tapestry 3.0 is the application engine, a core object typically configured once for the application. Here's a flavour of what you can extend or modify by overriding the appropriate methods in your AbstractEngine subclass:

    • property source: used to determine how configuration properties are loaded
    • page source: used to provide access to tapestry pages
    • specification source: used to provide access to page and component specification objects
    • template source: used to provide access to parsed page and component templates
    • data squeezer: used to handle conversions between Strings and Java objects
    • component class enhancer: used to enhance page and component classes, in particular to provide support for page and component properties

     

    This extensibility mechanism has been completely overhauled for Tapestry 4.0. The Tapestry 4.0 framework is now a collection of Hivemind services, many contributions to the tapestry.Infrastructure configuration point. Tapestry also exposes an tapestry.InfrastructureOverrides configuration point, specifically for the purposes of enhancing, decorating or replacing framework infrastructure services. The services that can be replaced include the ones described above, as well as many other new and existing ones. Various other parts of the framework, from persistence of properties to exception reporting, can also be extended or replaced by contributing to other Hivemind configuration points.

    JSF has a simple and well-defined extensibility model which has been present from the outset, and already used by a number of framework extensions. While it is possible to extend core Tapestry 3.0 framework services, the mechanism it uses is slightly clumsy, requiring subclassing of protected methods in AbstractEngine. Tapestry 4.0 is much more elegant and powerful, allowing framework extensions to be registered via entries to a hivemodule.xml file.

    A notable difference between JSF and Tapestry is that the JSF extension points are coarse-grained, while many of the Tapestry extension points are fine-grained. This has some benefits for Tapestry, but also makes it difficult to imagine how it would be possible, for example, to completely replace the display handling mechanism. Also, exposing extension points at a very low level would not be appropriate for JSF, because it would unnecessarily restrict vendor implementations, and introduce unnecessary complexity into the specification.

    JSF provides a more elegant extensibility mechanism than Tapestry 3.0. Tapestry 4.0 greatly improves on this feature of the framework.

    Other Issues

    I've discussed a variety of technical issues grouped neatly into individual categories. The next section considers a number of other issues, which don't fit neatly into any of the other previous categories or previously used format, but are still worthy of consideration when evaluating the two frameworks.

    Migration

    Users of Struts and other JSP Model 2 frameworks may find the move to JSF a less radical change. Struts users, for example, can migrate their applications step-by-step using the Struts-Faces Integration library, built using a set of JSF and Struts extensions. It's possible, although harder, to make existing JSP-based applications co-exist with Tapestry.

    Future Compatibility Issues

    JSF adoption is complicated by the dependencies with future releases of JSP versions. Substantial work is in progress to integrate the JSF and JSP expression languages. JSF 1.1 currently uses JSF EL, an expression language created specifically for JSF. From JSP 2.1 and JSF 1.2 these specifications will use a unified expression language. The current JSF EL will be deprecated, but still supported for backward compatibility. Taking advantage of some new JSF 1.2 features, such as JSTL integration, will require upgrading to a JSP 2.1 container, probably quite a major step in some cases. Backward compatibility issues from API changes appear more likely to arise for component developers than for application developers.

    The substantial changes underway with Tapestry are independent of JSP or Servlet specification changes. There are some backward incompatibilities, and in a number of other places existing mechanisms have been deprecated. Upgrading applications from Tapestry 3.0 to 4.0 will involve some effort.

    Portlets

    JSF has been designed from the ground up to support Portlets (JSR 168). Portlets are self-contained mini-applications running within an enclosing portal page. JSF implementations such as MyFaces provide support for portlets through minor configuration file changes. Tapestry 4.0 will support portlets for the first time, so users of the current version will need to upgrade to take advantage of this feature.

    Conclusion

    I've provided a fairly thorough but hopefully fair evaluation of JSF and Tapestry, comparing them head-to-head from a variety of perspectives, each relevant for web application development. The article arose from having to make a recommendation on which of the two frameworks to use in a real project. It's a task I can imagine many Java developers facing in the months to come.

    There are rich rewards to be gained from learning Tapestry. Tapestry 3.0 is impressive, and Tapestry 4.0 is looking even more like the finished article. Yet in spite of its merits, Tapestry is still not for mass consumption. If Tapestry is to get the success it deserves, it needs to find a way to broaden its base, in particular by making adoption easier for the average developer, and by removing a few quirks from its programming model. It would also benefit from more regular, incremental releases, with an emphasis on keeping the level of documentation more closely aligned with the introduction of new features.

    JSF is a powerful and extensible framework, and there seems little doubt that JSF will succeed. Yet in its current form it still has some rough edges. Steps aimed at fixing the JSP integration problems are in the right direction, but may take a while to materialize. In the meantime, it would be good to see the emergence of a compelling alternative to JSP templates, perhaps based on a Tapestry-style attribute-based model. Another welcome advance would be simpler component development, at least partly brought about by the introduction of templates into the component development model. At this stage, Facelets appears a promising contender for meeting these demands, and will certainly be worth observing in coming months.

    Biography

    Phil Zoio is an independent Java and J2EE developer and consultant based in Suffolk in the UK. His intestests include agile development techniques, Java open source frameworks, persistence and pretty much anything and everything, really. He can be contacted on philzoio@realsolve.co.uk.

    展开全文
  • The Best Day Of My Life

    2010-08-11 11:03:00
    Today, when I awoke, I suddenly realized that this is the best day of my life, ever! There were times when I wondered if I would make it to today; but I did! And because I did I'm going to ...

    Today, when I awoke, I suddenly realized that this is the best day of my life, ever! There were times when I wondered if I would make it to today; but I did! And because I did I'm going to celebrate!

    Today, I'm going to celebrate what an unbelievable life I have had so far: the accomplishments, the many blessings, and, yes, even the hardships because they have served to make me stronger. I will go through this day with my head held high, and a happy heart. I will marvel at God's seemingly simple gifts: the morning dew, the sun, the clouds, the trees, the flowers, the birds. Today, none of these miraculous creations will escape my notice.

    Today, I will share my excitement for life with other people. I'll make someone smile. I'll go out of my way to perform an unexpected act of kindness for someone I don't even know.

    Today, I'll give a sincere compliment to someone who seems down. I'll tell a child how special he is, and I'll tell someone I love just how deeply I care for her and how much she means to me.

    Today is the day I quit worrying about what I don't have and start being grateful for all the wonderful things God has already given me. I'll remember that to worry is just a waste of time because my faith in God and his Divine Plan ensures everything will be just fine.

    And tonight, before I go to bed, I'll go outside and raise my eyes to the heavens. I will stand in awe at the beauty of the stars and the moon, and I will praise God for these magnificent treasures.

    As the day ends and I lay my head down on my pillow, I will thank the Almighty for the best day of my life. And I will sleep the sleep of a contented child, excited with expectation because I know tomorrow is going to be the best day of my life, ever!

    展开全文
  • Life is short, as everyone knows. When I was a kid I used to wonder about this. Is life actually short, or are we really complaining about its finiteness? Would we be just as lik...
  • Adjusting to life abroad

    2016-10-20 17:42:51
    For example, we can guess that when someone at dinner points at a dish, tilts their head a bit, looks at us directly, raises their eyebrows, smiles and says something with the intonation of a ...
  • JS放在head和body的区别

    2018-09-07 15:04:00
    按照惯例脚本是放在head标签里的,但是因为浏览器会在遇到body标签时才会显示内容,那么把脚本放在head标签中需要加载,解析,执行所有的JavaScript代码才显示内容,会造成不好的用户体验,而放在body标签中会解决这...
  • Head_First_Python_笔记

    2017-07-31 14:43:53
    ['The Holy Grail', 1975,'The Life of Brian', 1979, 'The Meaning of Life', 1983] “”“ 处理列表数据 迭代 代码:“”“ >>>fav_movies=['The Holy Grail', 'The Life of Brian'] #定义一个列表...
  • Season 1, Episode 8: The Old Head -Michael: 17 days from now they strap my brother to an electric chair... strap: 用绳索捆扎 electric: 电的 17天后他要送我哥哥上电椅 send 50,000 bolts coursing ...
  • Life Cycle of Servlet 1) Loading of Servlet 2) Creating instance of Servlet 3) Invoke init() once 4) Invoke service() repeatedly for each client request 5) Invoke destroy() Step 1: Loading of Servlet ...
  • The Important Things in Life

    千次阅读 2008-01-17 15:39:00
    2006-04-07 23:53:01  Sometimes people come into your life and you know right away that they were meant to be there, to serve some sort of purpose, teach you a lesson, or to help you fig
  • Head First Python(Web开发)

    万次阅读 2014-12-15 19:59:39
    把程序放在Web上 Web应用有以下特点: ...能够访问你的网站的每一个人都可以使用这个程序。...第4步(可能)有很多子步骤:Web服务器要找到所有执行的程序、执行找到...>>> u_list(['Life of Brian','Holy Grail']) '...
  • 转自:https://developer.rackspace.com/blog/life-without-devstack-openstack-development-with-osa/ If you are an OpenStack contributor, you likely rely on DevStack for most of your work. DevStack is,...
  • Published on September 18, 2016 Bernard Marr Internationally best-selling author, keynote speaker, and strategic business Like 167,234 ...Keep your head down and your nose to the gri...
  • How to Change Your Life

    千次阅读 2012-04-16 14:47:32
    try isolating by writing down the first three things that come to mind "emotions, random thoughts, reflections on how you're feeling, or just the first three words that pop into your head." ...
  • Org Mode - Organize Your Life In Plain Text!

    千次阅读 2017-05-22 10:37:13
    原文链接 Table of Contents 1. How To Use This Document2. License3. Change History - What's new4. Getting Started ...4.1. Getting org-mode with Git4.2.... Organizing Your Life Into
  • “All of us have the power of choice. I choose to be rich, and I make that choice every day....You’re about to embark on this work/study program because you want to change your life. But before you...
  • Bill Gates' 11 Rules of Life

    2005-06-13 22:54:00
    Bill Gates' 11 Rules of Life Netlore Archive: Circulating via email, the text of a speech allegedly given by Bill Gates in which he sets out 11 rules of life for today's high school students BILL GA...
  • movies=['The Holy Grail','The life of brain','The meaning of life',2017,['dog','cat','duck']] 包括数字,字符串和列表,但数字和字符的组合只能包含在字符串之中。 列表中数据项的调用: print(movies[1]) ...
  • Programming Can Ruin Your Life

    千次阅读 2007-09-18 00:21:00
    You will constantly be mapping out flows and running the permutations through your head. Back in the real world, you will find yourself piecing together plans of breath-taking size and beauty that ...
  • Larger Than Life: Unit Join 300 ClubWA SHINGTON -- Convent never suit Randi Johnson anyway.all the nfl jerseys are of high quality at lowest price.000 ador fans,So it figur that he would
  • 转: Comparing Programming Languages in real life There are so many programming languages available that it can be very difficult to get to know them all well enough to pick the right one for you. On ...
  • http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-ManagementLinq to SQL uses a DataContext to manage it's access to the database as well as tracking changes m...
  • I consumed probably around 1200 calories per meal, 3 times a day plus snacks and sodas and energy drinks at 160+ calories per each. Normal Total Daily Calories Male: 2,000 to 3,000  ...
  • Star which might cover the whole army ten times the death who do not know deer inside your hand Warcraft sorry of course there is a strong effort in the difficult but weaknesses in the turnaround...
  • I consumed probably around 1200 calories per meal, 3 times a day plus snacks and sodas and energy drinks at 160+ calories per each. Normal Total Daily Calories Male: 2,000 to 3,000  Female: 1,...
  • What is Your “Average Speed” in Your Life, Your Health, and Your Work? by James Clear I have a friend namedNathan Barrywho recently finished writing three books in just 9 months. How di...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,802
精华内容 1,920
关键字:

headlifetimes