Reprinted: http://www.ibm.com/developerworks/cn/java/j-cb11076.html
A few years ago, I had the honor to teach my eldest daughter To Learn skiing. There is a rope in the tool provided by the ski school, which is used to tie the tip of the sled together. With this rope, beginners can easily perform ideal skiing operations, such as turning, slowing down, and stopping. Initially, these skiing players relied heavily on the rope. My daughter also vowed that she would not ski when she left the rope. Of course, she said that she didn't know the whole process because she had just started learning. This does not matter. Because I know that in the future, she will eventually force herself to break this constraint during skiing.
About this series
Currently, non-Java frameworks are affecting the Java framework construction method. Your concepts from other linguistics can affect Java programming. The Python (or Ruby, smalltalk, and other languages) code you write can also change the way Java code is written. Therefore
Crossing boundariesIn the series of articles, the author Bruce Tate put forward the idea that by learning other framework methods and languages, Java developers can better arm themselves!
This topic introduces completely different programming concepts and technologies from Java development, but these concepts and technologies can be directly applied to Java development. In some cases, these technologies need to be integrated to take advantage of them, but in other cases, these concepts can be applied directly. Here, independent tools are not important. What matters is the concepts and ideas that affect Java Community developers, frameworks, and even basic approaches in other languages and frameworks.
As a Java developer, I have had similar experiences. I love the security provided by static types. I once argued with Dave Thomas about the advantages of static type checks, but I was not convinced. Skiing is a completely different experience without the original "safety rope. For many people, the static type is also a dependency, which is no different from the bundle on the sled. Dave thinks that I just don't have enough experience to understand the benefits of Dynamic Language. When I realized the advantages of Ruby and smalltalk, I began to understand that I did not know enough about dynamic types, but it also deepened my understanding of them (see previous
Crossing boundaries
To obtain the overall comparison between static and dynamic types ). The biggest benefit to me is that it delays binding. This article uses Ruby, smalltalk, and a programming example of a derived language called self to explore the benefits of delayed binding.
Post-binding and pre-binding
The programming language can separate the declarations of functions (or methods in object-oriented languages) from their calls. You can declare a method and use a separate syntax to call this method, but the system needs to tie the two together. The process of binding a call and an implementation is calledBind. Binding to a type before implementation or binding to a type before implementation has a significant impact on the programming experience of a given language. Most object-oriented languages are later bound to the implementation to allowPolymorphismThis feature allows you to express many different child types as one type. Java code and C are mainly bound to a type in a previous compilation step. With this policy, the compiler has enough information to capture many different types
Bug, such as incompatibility between types of method parameters or return values. For example, the Code in Listing 1 produces several compilation errors:
Listing 1. Binding during compilation
... x = 1.0 + anObject.methodThatReturnsString(); anObject.methodRequiringIntParameter(aString);... |
By capturing errors during compilation, pre-binding helps to check problems early. There are many questions about the value of such assistance. Obviously, the type check is not enough to prove the correctness of the Code. Tests are required, and the problems found in such tests are often related to the type. However, such assistance is costly. When pre-binding is used, the compiler must make the following assumptions about the structure of the target object:
- You must have a predefined list of finite methods.
- The method to be called must exist during compilation.
- It must be bound to a known type, interface, or public superclass.
- The method and attribute of binding the target must be fixed.
The limitations of these assumptions may not be obvious at the beginning, but if you study them further, you will soon find the benefits of later binding, some of which areCrossing boundariesThe series has already introduced:
- The persistence framework of Ruby on Rails, active record, contains objects that do not have the compile-time attribute. After a database connection is established, rails adds an attribute to each row of the database at runtime to this model. The active record user does not always need to change the model object as the data model changes. It is later binding that makes all this possible.
- Unit testers must always write code that binds the test implementation (such as stub or mock) to the same method call as the product implementation. Java developers often useDependency InjectionTo solve this problem, this mode often requires complex frameworks, such as EJB or spring. Although these frameworks have many advantages, they also bring considerable complexity. This problem can also be solved by Aspect-oriented programming or an object factory with dependency lookup, but it also increases complexity. Test frameworks (such as Smalltalk and Ruby) in dynamic languages do not require dependency injection frameworks because they can be bound to the desired implementation at runtime. They are often able to achieve the same goal with a small piece of code.
- The entire Smalltalk language is built on the premise of delayed binding. Smalltalk developersImageBuilt on the continuous running applications. Because it is always running, any addition, deletion, or update operations on methods in any class occur at runtime. Delayed binding enables Smalltalk applications to run continuously throughout the development cycle.
Continuous Interval
Static or dynamic is only a point in a continuous interval. Some languages are highly static. Java is more dynamic than C or C ++. Each vertex in a continuous interval has its own compromise method. The Java language has many features that help to delay binding, all of which are at the cost of relatively high complexity. Reflection, dependency injection, and xml configuration can be used for delayed binding and reduced coupling. For a long time, Java makes it more dynamic by adding functions (such as Aspect-Oriented Programming. You may think that Java developers have everything they need. But there is another type of language-such as Smalltalk, self, and ruby-that is more dynamic than Java and allows a better way to delay binding.
These languages provide techniques not available in the Java language, such as overwriting the behavior that occurs when the method is lost. Remember that the Java language requires a method for binding during compilation. Other languagesOpen ClassThese classes can be changed based on the needs of developers. If you have been paying attention to the development of the framework for a long time, you will find that the demand for delayed binding is growing. This demand leads to many unnatural bindings in the Java language, they make the language complex and fuzzy. Other languages are waiting for us to build such frameworks to achieve higher abstraction levels and higher efficiency. For you, the advantage is obvious: You can get a language that is easier to express and more efficient.
To understand the points in a continuous interval, you can check the reflection. Using the Java language, you can find a method through reflection in the runtime class, verify the correct parameter settings for this method, and then execute this method. Many lines of code are required to implement these functions. However, these attempts to delay binding often outweighs the cost, so most Java application developers do not use this technology. Ruby, smalltalk, and self both use an original operation (such
object.send(method_name)
. This technology changes the nature of programming in these languages, and such examples can be seen everywhere.
The more in-depth study of the type policy and binding policy, the more you will find that binding to the call or type at run time will fundamentally change the programming process, thus opening up a new possible world. Yes, you will find that this is not so safe. However, you will also find that there are fewer duplicates, more powerful functions, and more flexibility while reducing code lines. To understand how all of this works, the following describes smalltalk, self, and Ruby. First, introduce the call method of delayed binding, and then introduce some available technologies that can change the class definition at runtime.
Back to Top
Delayed call
In a static language, the compiler directly binds the call to the implementation during compilation. Dynamic languages are somewhat different. Ruby, smalltalk, and self depend onMessage transmissionTo delay binding. The client uses message transmission to specify the target object, message, and parameter set. This is completely a running mechanism. Therefore, the dynamic language effectively adds Level 1 Indirect addressing. They bind the message name to an object, instead of binding the call to the type before implementation. Then, bind the object to a name or tag, and use the name or tag to find related implementations at runtime. It works like this:
- The client sends a message to the target object.
- The message has one name and zero or multiple parameters.
- The target (which can be a class or object) to find whether there is a method with the same name as this message.
- If yes, the target object calls this method.
- If no, the target object sends a message to the parent object. A parent object may be a super class (Smalltalk), a parent object (Self), or a module (Ruby ).
- If this method is not found in any parent object, an error capture method is called.
All the above steps occurRuntime. This means that no target method or implementation is required before the message statement is executed. In smalltalk, everything is an object, and most actions use message transmission. Even the control structure depends on it. Smalltalk has three types of messages:Mona1 message(No parameters ),Binary message(With fixed parameter sets) andKeyword message(With named parameters ). For example:
7 sin
Send a one-dollar messagesin
Send to target object7
.
3 + 4
Is a binary message. It will contain parameters4
Of+
Send messages to objects
3
.
array at: 1 put "value".
Is a keyword message. This code will send messagesat: put:
Send
array
Object, Setvalue
Placed in an array1
.
condition ifTrue: [doSomething] ifFalse: [doSomethingElse].
Is a keyword message. The code in parentheses is calledClosureOrCode block. This sample code will
:ifTrue :ifFalse
Message sentcondition
Object. If the condition is true, Smalltalk executes
[doSomething]
Code block; otherwise, execute[doSomethingElse]
Code block.
Ruby supports message transmission and direct method calls. Message transmission in ruby looks a little different, but the premise is consistent. In Listing 2speak
Method and
sleep
MethodDog
Class. Direct callspeak
Method and passsend
Method
name
Callsleep
Method.
Listing 2. Calling methods in Ruby in two ways
irb(main):001:0> class Dogirb(main):002:1> def speakirb(main):003:2> puts "Arf"irb(main):004:2> endirb(main):005:1> def sleepirb(main):006:2> puts "Zzz"irb(main):007:2> endirb(main):008:1> end=> nilirb(main):009:0> dog = Dog.new=> #<Dog:0x34fa9c>irb(main):010:0> dog.speakArf=> nilirb(main):011:0> message = "sleep"=> "sleep"irb(main):012:0> dog.send messageZzz |
If you are a Java programmer, these are nothing new. Of course, you can use the Java reflection API to process later binding. It only requires more efforts to implement the same function. However, the call method with any string (which can be modified through programming) does enable an additional function in the language. The most important thing is that it is easy to adjust any behavior that is added after the runtime.
The self language maximizes the concept of message transmission. In self, everything is an object. Call all self behaviors through message transmission. Self does not include classes (create new objects by copying other objects) or variables (only named slots with methods and objects ). Self uses message transmission to call named slots and methods. Most other object-oriented languages weaken the value of encapsulation by allowing direct access to instance data, but self overcomes this deficiency by enhancing the same protocol for access methods and instance data. Send a message to call a slot. If a slot has an object, the object returns its value. Self does not distinguish access attributes from access methods. This simplified measure allows
Self has become a simple but powerful programming language. Like smalltalk, self uses message transmission to represent the control structure, and Self depends on a continuously running image. All objects in self have a parent object and slots that contain other objects or methods. From the concept that self is heavily dependent on message transmission and the self application is always running, we can see that delayed binding is the central topic of self.
Message transmission in self is almost the same as in Smalltalk. In smalltalkcount <- 3
Convert numbers
3
Assigncount
. However, self has no variables and no value assignment. Message to be sentobject count: 3
Set
object
Ofcount
The slot value is set3
. To searchcount
Value, which can be used simply
object count
.
When youmethod_missing
After the message is added, the message transmission has another level of meaning. Keep in mind that this feature is open to dynamic languages and is completely disabled for languages bound to types during compilation. The advantage of early binding (forcing this method to exist) is also its core weakness. Ruby allows you to bypass
method_missing
Behavior to call a method that may not exist at runtime. Active record associates classes with tables and dynamically adds an attribute to each class according to each column in the database. Active record is also for each columnOr a set of ColumnsAutomatically add search program! For example, map
people
Database TablePerson
Class hasfirst_name
And
last_name
Column,person.find_by_first_name_and_last_name
Is a legal active record statement, although this method does not exist. Active record crossed
method_missing
Resolve the method name at runtime to determine whether the method name is valid. As a result, a very effective framework is generated for packaging database tables. This framework is greatly simplified by binding later. However, so far, I have only discussed the calling content. Now we can discuss the topic about adding behaviors.
Back to Top
Add behavior at runtime
All three languages (self, smalltalk, and Ruby) make it easy to add behavior at runtime. Using Self and smalltalk, any changes made to the existing class are implemented by defining a runtime modification. When a method is added, an active class is also effectively modified. Adding or deleting slots in self is simple: you only need to send
_add_slot
Message. Similarly, in Smalltalk, you can call the corresponding message (compile
) To add methods or properties. In both cases, you can directly modify a single copy of the class in the image. Next I will discuss how to add classes in Ruby in a little more depth.
Ruby frameworks often use several different mechanisms to modify classes at runtime. The simplest is to open a class. You can open any Ruby class and change it by renaming, adding, or deleting methods or properties. Suppose you want to extend the number in Ruby to simplify the implementation of the bar chart. You can open
Fixnum
Class, and add a method to print the corresponding length of the column, as shown in listing 3.
Listing 3. Extended fixnum
irb(main):001:0> 7.class=> Fixnumirb(main):002:0> class Fixnumirb(main):003:1> def barirb(main):004:2> puts('-'*self)irb(main):005:2> endirb(main):006:1> end=> nilirb(main):007:0> 7=> 7irb(main):008:0> 7.bar-------=> nilirb(main):009:0> [6, 8, 2, 4, 9].each {|i| i.bar}-----------------------------=> [6, 8, 2, 4, 9] |
This example shows how to extend the ruby class after you know the behavior when running the program. When you want to add arbitrary behavior to a Class Based on unknown rules, you need to use a different technique. You can estimate the string in the context of the class. Take Listing 4 as an example. Listing 3 expands
Fixnum
Class, which is considered static. In Listing 4, this class does not need to be considered static at all. PairDog
Allows you to add any behavior
dog
Or any other class. This class opens itself, adds a method with the name you specified, and adds the behavior you specified for this class:
Listing 4. extensible classes
class Dog def self.extend(method_name, method_body) class_eval(%Q[ def #{method_name} #{method_body} end ]) end def speak puts "Arf" end enddog = Dog.newDog.extend("sneeze", "puts 'Achoo!'")dog.speakdog.sneeze |
In Listing 4extend
The method must be further described. Here is a rough explanation. Ruby opens a class and adds a class
Dog
Class Name and the method of the subject. First, defineself.extend
.self.
Meaning
extend
Is a class method, that is, the method to operate on the entire class, suchnew
Method. Second, callclass_eval
. This method opens the class and executes the following strings on the opened class. Next, all
%[
And]
Code is interpreted as a single string. Finally, Ruby presses#{
And
}
This value is replaced by a variable.
Back to Top
Status Quo and beyond
In the example in Listing 4Dog
. Using the same technology, you can extend any Ruby class in the same way. Now, all the energy of delayed binding is highlighted. It can expand any function for a common class and call new behavior of the class, even though they do not exist when writing the original class.
The reflection function cannot be mentioned. Self, Ruby, and smalltalk are usually reflected. Its message transmission function allows you to call methods without forcing users to access physical methods, as in Java.class.methods
Provides an array of method names in ruby, while
class methods
Return the method set in Smalltalk. Using these features and similar features, you can find almost everything you need to perform quick self-check on classes or objects.
So far, I have discussed the content about binding to methods, but the content of delayed binding is much more. Let's take a look at the ruby method definition shown in listing 5.
Listing 5. Add two numbers
If you use a similar Java method, you need to enter a parameter. This ruby method returns the sum of two numbers. The only requirement for using this method is: the first object implementation+
The second object must be compatible with the first object. The client of this method can determine whether the object is compatible.
Similar Java methods are only applicable to single-type parameters. This ruby method can serve floating point, integer, string, and any support.+
Method type. Delayed binding can enable true polymorphism for a single method. This design is also scalable because it supports types that are not yet implemented by the current system. For example, this method can easily support virtual numbers.
Back to Top
Delayed binding in Java
The Java Community's fascination with static type checks is surprising, and Java programmers are spared no effort in finding ways to delay binding. Some methods are successful. Frameworks such as spring mainly exist for delayed binding, which helps to reduce the coupling between clients and services. Aspect-Oriented Programming implements delayed binding by providing services that can extend the functions of classes (or even exceed their current functions. Frameworks like hibernate can also be delayed and added to pure and common Java objects (pojo) at runtime through reflection, proxy, and other tools. There are many popular books on how to use pojo programming for developers' reference. Most of these books use more complex technologies (more advanced than reflection ), these technologies are mainly used to open classes and Delay Binding, thus effectively avoiding static types.
In other places, the delayed binding method is not so successful. There are many problems with the deployment descriptor that relies on XML to delay binding. The excessive dependency on XML is closely related to our strong desire for Dynamic Language behavior, because these languages are often a little too static and bound too early, and it is a little too restrictive.
There are already some languages and technologies that can provide solutions for these types of problems that Java programmers are eager to solve, for example, transparent durability, reduced coupling for testability, and richer plug-in models. Just look at the meta-program design that drives the development of the Java persistence framework and compare it with similar solutions in active record, Gemstone, or og (persistence framework in dynamic languages). (See
References ). Now, delayed binding becomes more and more important, and the ideas and practices that promote this process are also very efficient in other languages. When you need to design a metaprogram, open your toolbox and add several languages that allow delayed binding. Don't be afraid to cross borders!
References
Learning
- You can refer to
Original English.
- Java to Ruby: Things Your Manager shoshould know
(Tate, pragmatic bookshelf, 2006): The author of this article describes how to change from Java programming to Ruby on Rails and how to complete this transformation.
- Beyond Java
(Tate, O 'Reilly, 2005): The author of this article describes the origins and stable development of the Java language and the technology that can challenge the Java platform in some aspects.
- OG: an object-relational er in ruby. Because of the popularity of active record, it has not evolved, but its use model is very eye-catching.
- Gemstone: popular Smalltalk database.
- Smalltalk.org: community site of smalltalk.
- Rails up and running
(Tate, o'reilly, September 2006): An Introductory book on Ruby on Rails prepared by the author.
- "Book Review:
Agile web development with rails"(Darren torpey, developerworks, May 2005): This book introduces readers to understand the principles of rails and agile development.
- Spring framework: Spring is an excellent framework that overcomes the limitations of static types in Java.
- Programming Ruby
(Dave Thomas et al., pragmatic bookshelf, 2005): a best-selling book on Ruby programming.
- The rails API: rails framework document, which is an excellent reference for active record and its implementation technology.
- Java Technology Zone: hundreds of articles on various aspects of Java programming.
Obtain products and technologies
- Squeak: A Smalltalk implementation developed by Disney.
- SELF: Download the self programming language, which is a simplified version of smalltalk.
- Ruby on Rails: Download the open-source Ruby on Rails Web framework.
- Ruby: Obtain ruby from the project web site.
Discussion
- Developerworks blogs: Join the developerworks community.