学习日记可以考虑采用eclipse的理念

  整个平台是完全free的,可以在上面开发插件:比如,家教信息、专家咨询、方案设计、知识商品化...

  学习日记开发小组可以提供服务、提供插件开发。

  学习日记的前进需要注入资本的运营,虽然我们的目的不是为了赚钱,但离开了钱的支撑,学习日记只会灭亡。这是一定的。

  另外,大家加入学习日记开发小组的驱动支撑点是什么?这是需要努力探索的。否则,学习日记开发小组只会灭亡。这是一定的。

女儿会踩三轮车!

  两岁半的孩子,会踩三轮车!不是玩具三轮车,大人用的。你相信吗?哈哈,我的女儿会踩!

  她会非常灵活的上去,双手抓住龙头,两只脚一踩一踩的,三轮车就走了。我想,要是地方宽,骑上个20米恐怕都不是问题。

  当然,这还是有一定危险性的,毕竟她太小了,我们大人必须看着。但是,我不会禁止她去踩,这可以锻炼她的身体和培养勇敢的品格。

Inversion of Control Containers and the Dependency Injection

这篇文章看来也不错,还有中文的译文的pdf文档。贴在这里,以后慢慢看。

(转自:http://martinfowler.com/articles/injection.html)

 Home Blog Articles Books About Me Contact Me ThoughtWorks

Inversion of Control Containers and the Dependency Injection pattern

Martin Fowler

In the Java community there's been a rush of lightweight containers that help to assemble components from different projects into a cohesive application. Underlying these containers is a common pattern to how they perform the wiring, a concept they refer under the very generic name of "Inversion of Control". In this article I dig into how this pattern works, under the more specific name of "Dependency Injection", and contrast it with the Service Locator alternative. The choice between them is less important than the principle of separating configuration from use.

Last significant update: 23 Jan 04

| Chinese | Portuguese | French |

Contents

Components and Services

A Naive Example

Inversion of Control

Forms of Dependency Injection

Constructor Injection with PicoContainer

Setter Injection with Spring

Interface Injection

Using a Service Locator

Using a Segregated Interface for the Locator

A Dynamic Service Locator

Using both a locator and injection with Avalon

Deciding which option to use

Service Locator vs Dependency Injection

Constructor versus Setter Injection

Code or configuration files

Separating Configuration from Use

Some further issues

Concluding Thoughts

--------------------------------------------------------------------------------

One of the entertaining things about the enterprise Java world is the huge amount of activity in building alternatives to the mainstream J2EE technologies, much of it happening in open source. A lot of this is a reaction to the heavyweight complexity in the mainstream J2EE world, but much of it is also exploring alternatives and coming up with creative ideas. A common issue to deal with is how to wire together different elements: how do you fit together this web controller architecture with that database interface backing when they were built by different teams with little knowledge of each other.A number of frameworks have taken a stab at this problem, and several are branching out to provide a general capability to assemble components from different layers. These are often referred to as lightweight containers, examples include PicoContainer, and Spring.

Underlying these containers are a number of interesting design principles, things that go beyond both these specific containers and indeed the Java platform. Here I want to start exploring some of these principles. The examples I use are in Java, but like most of my writing the principles are equally applicable to other OO environments, particularly .NET.

--------------------------------------------------------------------------------

Components and Services

The topic of wiring elements together drags me almost immediately into the knotty terminology problems that surround the terms service and component. You find long and contradictory articles on the definition of these things with ease. For my purposes here are my current uses of these overloaded terms.

I use component to mean a glob of software that's intended to be used, without change, by application that is out of the control of the writers of the component. By 'without change' I mean that the using application doesn't change the source code of the components, although they may alter the component's behavior by extending it in ways allowed by the component writers.

A service is similar to a component in that it's used by foreign applications. The main difference is that I expect a component to be used locally (think jar file, assembly, dll, or a source import). A service will be used remotely through some remote interface, either synchronous or asynchronous (eg web service, messaging system, RPC, or socket.)

I mostly use service in this article, but much of the same logic can be applied to local components too. Indeed often you need some kind of local component framework to easily access a remote service. But writing "component or service" is tiring to read and write, and services are much more fashionable at the moment.

--------------------------------------------------------------------------------

A Naive Example

To help make all of this more concrete I'll use a running example to talk about all of this. Like all of my examples it's one of those super-simple examples; small enough to be unreal, but hopefully enough for you to visualize what's going on without falling into the bog of a real example.

In this example I'm writing a component that provides a list of movies directed by a particular director. This stunningly useful function is implemented by a single method.

class MovieLister...

    public Movie[] moviesDirectedBy(String arg) {

        List allMovies = finder.findAll();

        for (Iterator it = allMovies.iterator(); it.hasNext();) {

            Movie movie = (Movie) it.next();

            if (!movie.getDirector().equals(arg)) it.remove();

        }

        return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);

    }

The implementation of this function is naive in the extreme, it asks a finder object (which we'll get to in a moment) to return every film it knows about. Then it just hunts through this list to return those directed by a particular director. This particular piece of naivety I'm not going to fix, since it's just the scaffolding for the real point of this article.

The real point of this article is this finder object, or particularly how we connect the lister object with a particular finder object. The reason why this is interesting is that I want my wonderful moviesDirectedBy method to be completely independent of how all the movies are being stored. So all the method does is refer to a finder, and all that finder does is know how to respond to the findAll method. I can bring this out by defining an interface for the finder.

public interface MovieFinder {

    List findAll();

}

Now all of this is very well decoupled, but at some point I have to come up with a concrete class to actually come up with the movies. In this case I put the code for this in the constructor of my lister class.

class MovieLister...

  private MovieFinder finder;

  public MovieLister() {

    finder = new ColonDelimitedMovieFinder("movies1.txt");

  }

The name of the implementation class comes from the fact that I'm getting my list from a colon delimited text file. I'll spare you the details, after all the point is just that there's some implementation.

Now if I'm using this class for just myself, this is all fine and dandy. But what happens when my friends are overwhelmed by a desire for this wonderful functionality and would like a copy of my program? If they also store their movie listings in a colon delimited text file called "movies1.txt" then everything is wonderful. If they have a different name for their movies file, then it's easy to put the name of the file in a properties file. But what if they have a completely different form of storing their movie listing: a SQL database, an XML file, a web service, or just another format of text file? In this case we need a different class to grab that data. Now because I've defined a MovieFinder interface, this won't alter my moviesDirectedBy method. But I still need to have some way to get an instance of the right finder implementation into place.

Figure 3: The dependencies using a simple creation in the lister class

Figure 3 shows the dependencies for this situation. The MovieLister class is dependent on both the MovieFinder interface and upon the implementation. We would prefer it if it were only dependent on the interface, but then how do we make an instance to work with?

In my book P of EAA, we described this situation as a Plugin. The implementation class for the finder isn't linked into the program at compile time, since I don't know what my friends are going to use. Instead we want my lister to work with any implementation, and for that implementation to be plugged in at some later point, out of my hands. The problem is how can I make that link so that my lister class is ignorant of the implementation class, but can still talk to an instance to do its work.

Expanding this into a real system, we might have dozens of such services and components. In each case we can abstract our use of these components by talking to them through an interface (and using an adapter if the component isn't designed with an interface in mind). But if we wish to deploy this system in different ways, we need to use plugins to handle the interaction with these services so we can use different implementations in different deployments.

So the core problem is how do we assemble these plugins into an application? This is one of the main problems that this new breed of lightweight containers face, and universally they all do it using Inversion of Control.

--------------------------------------------------------------------------------

Inversion of Control

When these containers talk about how they are so useful because they implement "Inversion of Control" I end up very puzzled. Inversion of control is a common characteristic of frameworks, so saying that these lightweight containers are special because they use inversion of control is like saying my car is special because it has wheels.

The question, is what aspect of control are they inverting? When I first ran into inversion of control, it was in the main control of a user interface. Early user interfaces were controlled by the application program. You would have a sequence of commands like "Enter name", "enter address"; your program would drive the prompts and pick up a response to each one. With graphical (or even screen based) UIs the UI framework would contain this main loop and your program instead provided event handlers for the various fields on the screen. The main control of the program was inverted, moved away from you to the framework.

For this new breed of containers the inversion is about how they lookup a plugin implementation. In my naive example the lister looked up the finder implementation by directly instantiating it. This stops the finder from being a plugin. The approach that these containers use is to ensure that any user of a plugin follows some convention that allows a separate assembler module to inject the implementation into the lister.

As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.

I'm going to start by talking about the various forms of dependency injection, but I'll point out now that that's not the only way of removing the dependency from the application class to the plugin implementation. The other pattern you can use to do this is Service Locator, and I'll discuss that after I'm done with explaining Dependency Injection.

--------------------------------------------------------------------------------

Forms of Dependency Injection

The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface, resulting in a dependency diagram along the lines of Figure 1

Figure 1: The dependencies for a Dependency Injector

There are three main styles of dependency injection. The names I'm using for them are Constructor Injection, Setter Injection, and Interface Injection. If you read about this stuff in the current discussions about Inversion of Control you'll hear these referred to as type 1 IoC (interface injection), type 2 IoC (setter injection) and type 3 IoC (constructor injection). I find numeric names rather hard to remember, which is why I've used the names I have here.

Constructor Injection with PicoContainer

I'll start with showing how this injection is done using a lightweight container called PicoContainer. I'm starting here primarily because several of my colleagues at ThoughtWorks are very active in the development of PicoContainer (yes, it's a sort of corporate nepotism.)

PicoContainer uses a constructor to decide how to inject a finder implementation into the lister class. For this to work, the movie lister class needs to declare a constructor that includes everything it needs injected.

class MovieLister...

    public MovieLister(MovieFinder finder) {

        this.finder = finder;      

    }

The finder itself will also be managed by the pico container, and as such will have the filename of the text file injected into it by the container.

class ColonMovieFinder...

    public ColonMovieFinder(String filename) {

        this.filename = filename;

    }

The pico container then needs to be told which implementation class to associate with each interface, and which string to inject into the finder.

    private MutablePicoContainer configureContainer() {

        MutablePicoContainer pico = new DefaultPicoContainer();

        Parameter[] finderParams =  {new ConstantParameter("movies1.txt")};

        pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);

        pico.registerComponentImplementation(MovieLister.class);

        return pico;

    }

This configuration code is typically set up in a different class. For our example, each friend who uses my lister might write the appropriate configuration code in some setup class of their own. Of course it's common to hold this kind of configuration information in separate config files. You can write a class to read a config file and set up the container appropriately. Although PicoContainer doesn't contain this functionality itself, there is a closely related project called NanoContainer that provides the appropriate wrappers to allow you to have XML configuration files. Such a nano container will parse the XML and then configure an underlying pico container. The philosophy of the project is to separate the config file format from the underlying mechanism.

To use the container you write code something like this.

    public void testWithPico() {

        MutablePicoContainer pico = configureContainer();

        MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class);

        Movie[] movies = lister.moviesDirectedBy("Sergio Leone");

        assertEquals("Once Upon a Time in the West", movies[0].getTitle());

    }

Although in this example I've used constructor injection, PicoContainer also supports setter injection, although it's developers do prefer constructor injection.

Setter Injection with Spring

The Spring framework is a wide ranging framework for enterprise Java development. It includes abstraction layers for transactions, persistence frameworks, web application development and JDBC. Like PicoContainer it supports both constructor and setter injection, but its developers tend to prefer setter injection - which makes it an appropriate choice for this example.

To get my movie lister to accept the injection I define a setting method for that service

class MovieLister...

    private MovieFinder finder;

  public void setFinder(MovieFinder finder) {

    this.finder = finder;

  }

Similarly I define a setter for the string the finder.

class ColonMovieFinder...

    public void setFilename(String filename) {

        this.filename = filename;

    }

The third step is to set up the configuration for the files. Spring supports configuration through XML files and also through code, but XML is the expected way to do it.

    <beans>

        <bean id="MovieLister" class="spring.MovieLister">

            <property name="finder">

                <ref local="MovieFinder"/>

            </property>

        </bean>

        <bean id="MovieFinder" class="spring.ColonMovieFinder">

            <property name="filename">

                <value>movies1.txt</value>

            </property>

        </bean>

    </beans>

The test then looks like this.

    public void testWithSpring() throws Exception {

        ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");

        MovieLister lister = (MovieLister) ctx.getBean("MovieLister");

        Movie[] movies = lister.moviesDirectedBy("Sergio Leone");

        assertEquals("Once Upon a Time in the West", movies[0].getTitle());

    }

Interface Injection

The third injection technique is to define and use interfaces for the injection. Avalon is an example of a framework that uses this technique in places. I'll talk a bit more about that later, but in this case I'm going to use it with some simple sample code.

With this technique I begin by defining an interface that I'll use to perform the injection through. Here's the interface for injecting a movie finder into an object.

public interface InjectFinder {

    void injectFinder(MovieFinder finder);

}

This interface would be defined by whoever provides the MovieFinder interface. It needs to be implemented by any class that wants to use a finder, such as the lister.

class MovieLister implements InjectFinder...

    public void injectFinder(MovieFinder finder) {

        this.finder = finder;

    }

I use a similar approach to inject the filename into the finder implementation.

public interface InjectFinderFilename {

    void injectFilename (String filename);

}

class ColonMovieFinder implements MovieFinder, InjectFinderFilename......

    public void injectFilename(String filename) {

        this.filename = filename;

    }

Then, as usual, I need some configuration code to wire up the implementations. For simplicity's sake I'll do it in code.

class Tester...

    private Container container;

     private void configureContainer() {

       container = new Container();

       registerComponents();

       registerInjectors();

       container.start();

    }

This configuration has two stages, registering components through lookup keys is pretty similar to the other examples.

class Tester...

  private void registerComponents() {

    container.registerComponent("MovieLister", MovieLister.class);

    container.registerComponent("MovieFinder", ColonMovieFinder.class);

  }

A new step is to register the injectors that will inject the dependent components. Each injection interface needs some code to inject the dependent object. Here I do this by registering injector objects with the container. Each injector object implements the injector interface.

class Tester...

  private void registerInjectors() {

    container.registerInjector(InjectFinder.class, container.lookup("MovieFinder"));

    container.registerInjector(InjectFinderFilename.class, new FinderFilenameInjector());

  }

public interface Injector {

  public void inject(Object target);

}

When the dependent is a class written for this container, it makes sense for the component to implement the injector interface itself, as I do here with the movie finder. For generic classes, such as the string, I use an inner class within the configuration code.

class ColonMovieFinder implements Injector......

  public void inject(Object target) {

    ((InjectFinder) target).injectFinder(this);       

  }

class Tester...

  public static class FinderFilenameInjector implements Injector {

    public void inject(Object target) {

      ((InjectFinderFilename)target).injectFilename("movies1.txt");     

    }

    }

The tests then use the container.

class IfaceTester...

    public void testIface() {

      configureContainer();

      MovieLister lister = (MovieLister)container.lookup("MovieLister");

      Movie[] movies = lister.moviesDirectedBy("Sergio Leone");

      assertEquals("Once Upon a Time in the West", movies[0].getTitle());

    }

The container uses the declared injection interfaces to figure out the dependendencies and the injectors to inject the correct dependents. (The specific container implementation I did here isn't important to the technique, and I won't show it because you'd only laugh.)

--------------------------------------------------------------------------------

Using a Service Locator

The key benefit of a Dependency Injector is that it removes the dependency that the MovieLister class has on the concrete MovieFinder implementation. This allows me to give listers to friends and for them to plug in a suitable implementation for their own environment. Injection isn't the only way to break this dependency, another is to use a service locator.

The basic idea behind a service locator is to have an object that knows how to get hold of all of the services that an application might need. So a service locator for this application would have a method that returns a movie finder when one is needed. Of course this just shifts the burden a tad, we still have to get the locator into the lister, resulting in the dependencies of Figure 2

Figure 2: The dependencies for a Service Locator

In this case I'll use the ServiceLocator as a singleton Registry. The lister can then use that to get the finder when it's instantiated.

class MovieLister...

    MovieFinder finder = ServiceLocator.movieFinder();

class ServiceLocator...

    public static MovieFinder movieFinder() {

        return soleInstance.movieFinder;

    }

    private static ServiceLocator soleInstance;

    private MovieFinder movieFinder;

As with the injection approach, we have to configure the service locator. Here I'm doing it in code, but it's not hard to use a mechanism that would read the appropriate data from a configuration file.

class Tester...

    private void configure() {

        ServiceLocator.load(new ServiceLocator(new ColonMovieFinder("movies1.txt")));

    }

class ServiceLocator...

    public static void load(ServiceLocator arg) {

        soleInstance = arg;

    }

    public ServiceLocator(MovieFinder movieFinder) {

        this.movieFinder = movieFinder;

    }

Here's the test code.

class Tester...

    public void testSimple() {

        configure();

        MovieLister lister = new MovieLister();

        Movie[] movies = lister.moviesDirectedBy("Sergio Leone");

        assertEquals("Once Upon a Time in the West", movies[0].getTitle());

    }

I've often heard the complaint that these kinds of service locators are a bad thing because they aren't testable because you can't substitute implementations for them. Certainly you can design them badly to get into this kind of trouble, but you don't have to. In this case the service locator instance is just a simple data holder. I can easily create the locator with test implementations of my services.

For a more sophisticated locator I can subclass service locator and pass that subclass into the registry's class variable. I can change the static methods to call a method on the instance rather accessing instance variables directly. I can provide thread specific locators by using thread specific storage. All of this can be done without changing clients of service locator.

A way to think of this is that service locator is a registry not a singleton. A singleton provides a simple way of implementing a registry, but that implementation decision is easily changed.

Using a Segregated Interface for the Locator

One of the issues with the simple approach above, is that the MovieLister is dependent on the full service locator class, even though it only uses one service. We can reduce this by using a segregated interface. That way, instead of using the full service locator interface, the lister can declare just the bit of interface it needs.

In this situation the provider of the lister would also provide a locator interface which it needs to get hold of the finder.

public interface MovieFinderLocator {

    public MovieFinder movieFinder();

The locator then needs to implement this interface to provide access to a finder.

    MovieFinderLocator locator = ServiceLocator.locator();

    MovieFinder finder = locator.movieFinder();

   public static ServiceLocator locator() {

        return soleInstance;

    }

    public MovieFinder movieFinder() {

        return movieFinder;

    }

    private static ServiceLocator soleInstance;

    private MovieFinder movieFinder;

You'll notice that since we want to use an interface, we can't just access the services through static methods any more. We have to use the class to get a locator instance and then use that to get what we need.

A Dynamic Service Locator

The above example was static, in that the service locator class has methods for each of the services that you need. This isn't the only way of doing it, you can also make a dynamic service locator that allows you to stash any service you need into it and make your choices at runtime.

In this case, the service locator uses a map instead of fields for each of the services, and provides generic methods to get and load services.

class ServiceLocator...

    private static ServiceLocator soleInstance;

    public static void load(ServiceLocator arg) {

        soleInstance = arg;

    }

    private Map services = new HashMap();

    public static Object getService(String key){

        return soleInstance.services.get(key);

    }

    public void loadService (String key, Object service) {

        services.put(key, service);

    }

Configuring involves loading a service with an appropriate key.

class Tester...

    private void configure() {

        ServiceLocator locator = new ServiceLocator();

        locator.loadService("MovieFinder", new ColonMovieFinder("movies1.txt"));

        ServiceLocator.load(locator);

    }

I use the service by using the same key string.

class MovieLister...

    MovieFinder finder = (MovieFinder) ServiceLocator.getService("MovieFinder");

On the whole I dislike this approach. Although it's certainly flexible, it's not very explicit. The only way I can find out how to reach a service is through textual keys. I prefer explicit methods because it's easier to find where they are by looking at the interface definitions.

Using both a locator and injection with Avalon

Dependency injection and a service locator aren't necessarily mutually exclusive concepts. A good example of using both together is the Avalon framework. Avalon uses a service locator, but uses injection to tell components where to find the locator.

Berin Loritsch sent me this simple version of my running example using Avalon.

public class MyMovieLister implements MovieLister, Serviceable {

    private MovieFinder finder;

    public void service( ServiceManager manager ) throws ServiceException {

        finder = (MovieFinder)manager.lookup("finder");

    }

     

The service method is an example of interface injection, allowing the container to inject a service manager into MyMovieLister. The service manager is an example of a service locator. In this example the lister doesn't store the manager in a field, instead it immediately uses it to lookup the finder, which it does store.

--------------------------------------------------------------------------------

Deciding which option to use

So far I've concentrated on explaining how I see these patterns and their variations. Now I can start talking about their pros and cons to help figure out which ones to use and when.

Service Locator vs Dependency Injection

The fundamental choice is between Service Locator and Dependency Injection. The first point is that both implementations provide the fundamental decoupling that's missing in the naive example - in both cases application code is independent of the concrete implementation of the service interface. The important difference between the two patterns is about how that implementation is provided to the application class. With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class - hence the inversion of control.

Inversion of control is a common feature of frameworks, but it's something that comes at a price. It tends to be hard to understand and leads to problems when you are trying to debug. So on the whole I prefer to avoid it unless I need it. This isn't to say it's a bad thing, just that I think it needs to justify itself over the more straightforward alternative.

The key difference is that with a Service Locator every user of a service has a dependency to the locator. The locator can hide dependencies to other implementations, but you do need to see the locator. So the decision between locator and injector depends on whether that dependency is a problem.

Using dependency injection can help make it easier to see what the component dependencies are. With dependency injector you can just look at the injection mechanism, such as the constructor, and see the dependencies. With the service locator you have to search the source code for calls to the locator. Modern IDEs with a find references feature make this easier, but it's still not as easy as looking at the constructor or setting methods.

A lot of this depends on the nature of the user of the service. If you are building an application with various classes that use a service, then a dependency from the application classes to the locator isn't a big deal. In my example of giving a Movie Lister to my friends, then using a service locator works quite well. All they need to do is to configure the locator to hook in the right service implementations, either through some configuration code or through a configuration file. In this kind of scenario I don't see the injector's inversion as providing anything compelling.

The difference comes if the lister is a component that I'm providing to an application that other people are writing. In this case I don't know much about the APIs of the service locators that my customers are going to use. Each customer might have their own incompatible service locators. I can get around around some of this by using the segregated interface. Each customer can write an adapter that matches my interface to their locator, but in any case I still need to see the first locator to lookup my specific interface. And once the adapter appears then the simplicity of the direct connection to a locator is beginning to slip.

Since with an injector you don't have a dependency from a component to the injector, the component cannot obtain further services from the injector once it's been configured.

A common reason people give for preferring dependency injection is that it makes testing easier. The point here is that to do testing, you need to easily replace real service implementations with stubs or mocks. However there is really no difference here between dependency injection and service locator: both are very amenable to stubbing. I suspect this observation comes from projects where people don't make the effort to ensure that their service locator can be easily substituted. This is where continual testing helps, if you can't easily stub services for testing, then this implies a serious problem with your design.

Of course the testing problem is exacerbated by component environments that are very intrusive, such as Java's EJB framework. My view is that these kinds of frameworks should minimize their impact upon application code, and particularly should not do things that slow down the edit-execute cycle. Using plugins to substitute heavyweight components does a lot help this process, which is vital for practices such as Test Driven Development.

So the primary issue is for people who are writing code that expects to be used in applications outside of the control of the writer. In these cases even a minimal assumption about a Service Locator is a problem.

Constructor versus Setter Injection

For service combination, you always have to have some convention in order to wire things together. The advantage of injection is primarily that it requires very simple conventions - at least for the constructor and setter injections. You don't have to do anything odd in your component and it's fairly straightforward for an injector to get everything configured.

Interface injection is more invasive since you have to write a lot of interfaces to get things all sorted out. For a small set of interfaces required by the container, such as in Avalon's approach, this isn't too bad. But it's a lot of work for assembling components and dependencies, which is why the current crop of lightweight containers go with setter and constructor injection.

The choice between setter and constructor injection is interesting as it mirrors a more general issue with object-oriented programming - should you fill fields in a constructor or with setters.

My long running default with objects is as much as possible, to create valid objects at construction time. This advice goes back to Kent Beck's Smalltalk Best Practice Patterns: Constructor Method and Constructor Parameter Method. Constructors with parameters give you a clear statement of what it means to create a valid object in an obvious place. If there's more than one way to do it, create multiple constructors that show the different combinations.

Another advantage with constructor initialization is that it allows you to clearly hide any fields that are immutable by simply not providing a setter. I think this is important - if something shouldn't change then the lack of a setter communicates this very well. If you use setters for initialization, then this can become a pain. (Indeed in these situations I prefer to avoid the usual setting convention, I'd prefer a method like initFoo, to stress that it's something you should only do at birth.)

But with any situation there are exceptions. If you have a lot of constructor parameters things can look messy, particularly in languages without keyword parameters. It's true that a long constructor is often a sign of an over-busy object that should be split, but there are cases when that's what you need.

If you have multiple ways to construct a valid object, it can be hard to show this through constructors, since constructors can only vary on the number and type of parameters. This is when Factory Methods come into play, these can use a combination of private constructors and setters to implement their work. The problem with classic Factory Methods for components assembly is that they are usually seen as static methods, and you can't have those on interfaces. You can make a factory class, but then that just becomes another service instance. A factory service is often a good tactic, but you still have to instantiate the factory using one of the techniques here.

Constructors also suffer if you have simple parameters such as strings. With setter injection you can give each setter a name to indicate what the string is supposed to do. With constructors you are just relying on the position, which is harder to follow.

If you have multiple constructors and inheritance, then things can get particularly awkward. In order to initialize everything you have to provide constructors to forward to each superclass constructor, while also adding you own arguments. This can lead to an even bigger explosion of constructors.

Despite the disadvantages my preference is to start with constructor injection, but be ready to switch to setter injection as soon as the problems I've outlined above start to become a problem.

This issue has led to a lot of debate between the various teams who provide dependency injectors as part of their frameworks. However it seems that most people who build these frameworks have realized that it's important to support both mechanisms, even if there's a preference for one of them.

Code or configuration files

A separate but often conflated issue is whether to use configuration files or code on an API to wire up services. For most applications that are likely to be deployed in many places, a separate configuration file usually makes most sense. Almost all the time this will be an XML file, and this makes sense. However there are cases where it's easier to use program code to do the assembly. One case is where you have a simple application that's not got a lot of deployment variation. In this case a bit of code can be clearer than separate XML file.

A contrasting case is where the assembly is quite complex, involving conditional steps. Once you start getting close to programming language then XML starts breaking down and it's better to use a real language that has all the syntax to write a clear program. You then write a builder class that does the assembly. If you have distinct builder scenarios you can provide several builder classes and use a simple configuration file to select between them.

I often think that people are over-eager to define configuration files. Often a programming language makes a straightforward and powerful configuration mechanism. Modern languages can easily compile small assemblers that can be used to assemble plugins for larger systems. If compilation is a pain, then there are scripting languages that can work well also.

It's often said that configuration files shouldn't use a programing language because they need to be edited by non-programmers. But how often is this the case? Do people really expect non-programmers to alter the transaction isolation levels of complex server-side application? Non-language configuration files work well only to the extent they are simple. If they become complex then it's time to think about using a proper programming language.

One thing we're seeing in the Java world at the moment is a cacophony of configuration files, where every component has its own configuration files which are different to everyone else's. If you use a dozen of these components, you can easily end up with a dozen configuration files to keep in sync.

My advice here is to always provide a way to do all configuration easily with a programmatic interface, and then treat a separate configuration file as an optional feature. You can easily build configuration file handling to use the programmatic interface. If you are writing a component you then leave it up to your user whether to use the programmatic interface, your configuration file format, or to write their own custom configuration file format and tie it into the programmatic interface

Separating Configuration from Use

The important issue in all of this is to ensure that the configuration of services is separated from their use. Indeed this is a fundamental design principle that sits with the separation of interfaces from implementation. It's something we see within an object-oriented program when conditional logic decides which class to instantiate, and then future evaluations of that conditional are done through polymorphism rather than through duplicated conditional code.

If this separation is useful within a single code base, it's especially vital when you're using foreign elements such as components and services. The first question is whether you wish to defer the choice of implementation class to particular deployments. If so you need to use some implementation of plugin. Once you are using plugins then it's essential that the assembly of the plugins is done separately from the rest of the application so that you can substitute different configurations easily for different deployments. How you achieve this is secondary. This configuration mechanism can either configure a service locator, or use injection to configure objects directly.

--------------------------------------------------------------------------------

Some further issues

In this article, I've concentrated on the basic issues of service configuration using Dependency Injection and Service Locator. There are some more topics that play into this which also deserve attention, but I haven't had time yet to dig into. In particular there is the issue of life-cycle behavior. Some components have distinct life-cycle events: stop and starts for instance. Another issue is the growing interest in using aspect oriented ideas with these containers. Although I haven't considered this material in the article at the moment, I do hope to write more about this either by extending this article or by writing another.

You can find out a lot more about these ideas by looking at the web sites devoted to the lightweight containers. Surfing from the picocontainer and spring web sites will lead to you into much more discussion of these issues and a start on some of the further issues.

--------------------------------------------------------------------------------

Concluding Thoughts

The current rush of lightweight containers all have a common underlying pattern to how they do service assembly - the dependency injector pattern. Dependency Injection is a useful alternative to Service Locator. When building application classes the two are roughly equivalent, but I think Service Locator has a slight edge due to its more straightforward behavior. However if you are building classes to used in multiple applications then Dependency Injection is a better choice.

If you use Dependency Injection there are a number of styles to choose between. I would suggest you follow constructor injection unless you run into into one of the specific problems with that approach, in which case switch to setter injection. If you are choosing to build or obtain a container, look for one that supports both constructor and setter injection.

The choice between Service Locator and Dependency Injection is less important than the principle of separating service configuration from the use of services within an application.

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

Acknowledgements

My sincere thanks to the many people who've helped me with this article. Rod Johnson, Paul Hammant, Joe Walnes, Aslak Helles?on Tirs鮠and Bill Caputo helped me get to grips with these concepts and commented on the early drafts of this article. Berin Loritsch and Hamilton Verissimo de Oliveira provided some very helpful advice on how Avalon fits in. Dave W Smith persisted in asking questions about my initial interface injection configuration code and thus made me confront the fact that it was stupid.

Significant Revisions

23 Jan 04: Redid the configuration code of the interface injection example.

16 Jan 04: Added a short example of both locator and injection with Avalon.

14 Jan 04: First Publication

--------------------------------------------------------------------------------

   

--------------------------------------------------------------------------------

© Copyright Martin Fowler, all rights reserved

(转帖)Spring Gossip: Inversion of Control

在翻译一篇涉及Spring的文章,里面有个概念叫作:Inversion of Control,我觉得这篇文章解释很透。就贴在这里了。

(转自:http://caterpillar.onlyfun.net/Gossip/SpringGossip/IOC.html)

现在还没有专门的计划去学习Spring,就把它统归在“学习java”这个大的分类里,以后再转到合适的目标下。(需要完善日记重新归类的功能)

From Gossip@caterpillar

Spring Gossip: Inversion of Control

Spring 的核心概念是 IoC,IoC 的抽象概念是「依賴關係的轉移」,像是「高層模組不應該依賴低層模組,而是模組都必須依賴於抽象」是 IoC 的一種表現,「實現必須依賴抽象,而不是抽象依賴實現」也是 IoC 的一種表現,「應用程式不應依賴於容器,而是容器服務於應用程式」也是 IoC 的一種表現。

IoC 全名 Inversion of Control,如果中文硬要翻譯過來的話,就是「控制反轉」。初看 IoC,從字面上不容易瞭解其意義,我覺得要瞭解 IoC,要先從 Dependency Inversion 開始瞭解,也就是依賴關係的反轉。

Dependency Inversion The Dependency Inversion Principle 有清楚的解釋。

簡單的說,在模組設計時,高層的抽象模組通常是與業務相關的模組,它應該具有重用性,而不依賴於低層的實作模組,例如如果低層模組原先是軟碟存取模式,而高層模組是個存檔備份的需求,如果高層模組直接叫用低層模組的函式,則就對低層模組產生了依賴關係。

舉個例子,例如下面這個程式:

#include <floppy.h>

....

void save() {

        ....

        saveToFloppy()

    }

}

由於save()程式依賴於依賴於saveToFloppy(),如果今天要更換低層的存儲模組為Usb碟,則這個程式沒有辦法重用,必須加以修改才行,低層模組的更動造成了高層模組也必須跟著更動,這不是一個好的設計方式,在設計上希望模組都依賴於模組的抽象,這樣才可以重用高層的業務設計。

如果以物件導向的方式來設計,依賴反轉(Dependency Inversion)的解釋變為程式不應依賴實作,而是依賴於抽象,實作必須依賴於抽象。來看看下面這個 Java 程式:

public class BusinessObject {

    private FloppyWriter writer = new FloppyWriter();

    ....

   

    public void save() {

        ...

        writer.saveToFloppy();

    }

}

在這個程式中,BusinessObject 的存檔依賴於實際的 FloppyWriter,如果今天想要將存檔改為存至 Usb 碟,則必須修改或繼承 BusinessObject 進行擴展,而無法直接使用BusinessObject。

如果透過介面的宣告,可以改進此一情況,例如:

public interface IDeviceWriter {

    public void saveToDevice();

}

public class BusinessObject {

    private IDeviceWriter writer;

    public void setDeviceWriter(IDeviceWriter writer) {

        this.writer = writer;

    }

    public void save() {

        ....

        writer.saveToDevice();

    }

}

這樣一來,BusinessObject 就是可重用的,如果今天有存儲至 Floppy 或 Usb 碟的需求,只要實作 IDeviceWriter 即可,而不用修改 BusinessObject:

public class FloppyWriter implement IDeviceWriter {

    public void saveToDevice() {

        ....

        // 實際儲存至Floppy的程式碼

    }

}

public class UsbDiskWriter implement IDeviceWriter {

    public void saveToDevice() {

        ....

        // 實際儲存至UsbDisk的程式碼

    }

}

從這個角度來看,Dependency Inversion 的意思即是程式不依賴於實作,而是程式與實作都要依賴於抽象。

IoC 的 Control 是控制的意思,其實其背後的意義也是一種依賴關係的轉移,如果A依賴於B,其意義即是B擁有控制權,您想要轉移這種關係,所以依賴關係的反轉即是控制關係的反轉,藉由控制關係的轉移,可以獲得元件的可重用性,在上面的 Java 程式中,整個控制權從實際的 FloppyWriter 轉移至抽象的 IDeviceWriter 介面上,使得BusinessObject、FloppyWriter、UsbDiskWriter 這幾個實現依賴於抽象的 IDeviceWriter 介面。

程式的業務邏輯部份應是可以重用的,不應受到所使用框架或容器的影響,因為可能轉移整個業務邏輯至其它的框架或容器,如果業務邏輯過於依賴容器,則轉移至其它的框架或容器時,就會發生困難。

IoC 在容器的角度,可以用這麼一句好萊塢名言來代表:"Don't call me, I'll call you." 以程式的術語來說的話,就是「不要向容器要求您所需要的(物件)資源,容器會自動將這些物件給您!」。IoC 要求的是容器不侵入應用程式本身,應用程式本身提供好介面,容器可以透過這些介面將所需的資源注至至程式中,應用程式不向容器主動要求資源,故而不會依賴於容器的元件,應用程式本身不會意識到正被容器使用,可以隨時從容器中脫離轉移而不用作任何的修改,而這個特性正是一些業務邏輯中間件最需要的。

不要打消孩子的参与劳动的积极性

  看到大人做家务,比如:扫地、做饭,孩子总是在旁边要来帮忙。虽然实际上是添乱,等她长大了,叫她做还不做呢。

  但是,我觉得不要伤害孩子的参与积极性。她感兴趣是好事,可以叫她做些她能做的事,比如:端空碗、拿东西、喊人吃饭。每当给她布置这种任务,她总是高兴的去做。

  我感觉我的做法是对的。

(转帖)《UML:Java程序员指南》的读书笔记

转自:http://www.uml.org.cn/UMLApplication/200509273.htm

 UML软件工程组织     北京火龙果软件工程技术中心

 

--------------------------------------------------------------------------------

《UML:Java程序员指南》的读书笔记

来自:http://blog.csdn.net

1.  第一章 针对Java程序员的UML概述

    1.  UML(统一建模语言)的三个层次:

        1.  概念层(Conceptual)

            接近人类自然语言

            有歧义

            无严格的格式

          

        2.  规格说明层(Specification) 

        3.  实现层(Implementation)   

             规格说明层和实现层接近代码、无歧义、有严格的格式

                                           

    2.  UML分三类:

        1.  静态图(static diagrams)

            描绘类、对象、数据结构以及存在于它们之间的逻辑关系。

      

        2.  动态图(dynamic diagrams)

            描绘运行期间,软件执行流程和软件实体状态改变的方式。

      

        3.  物理图(physical diagrams)

            描绘物理实体,如:源文件、库文件、字节文件、数据文件等,以及它们之间存在的逻辑关系。

          

    3.  类图(静态图)

        1.  长方形表示类,箭头表示关系。

        2.  图中的所有关系叫关联(associatons),关联的命名对应引用的对象的变量名称。

      

    4.  对象图(动态图)

        可以看成是内存的一个快照,主要反应程序中的对象在运行期的信息。

        1.  对象名称有下划线。冒号后面跟着对象类型的名称。

      

        2.  对象之间的关系叫链接(links),链接的命名同样对应引用的对象的变量名称。

      

    5.  序列图(动态图)

        几个术语:

        1.  监护(guards)

      

        2.  构造(construction)

      

        3.  数据标记(data token)

      

        4.  活动(activation)

      

    6.  协作图(动态图)

        序列数的点结构

      

    7.  协作图和序列图的异同点

        同:它们包含的信息相同

        异:协作图描述对象之间的关系,序列图描述消息被执行的前后顺序。

      

    8.  状态图(动态图)

        限定状态机(finate state machines)

        图中的箭头被称为转换(transitions)

  

2.  第二章 使用图

    1.  为何要制作模型?

        制作模型的目的是为了证明模型是否可以正常工作。

        一个模型必须有一个可用的检验标准。

      

    2.  使用UML的时机:

        1.  需要通过检验来确定某些东西是否可用的时候。

        2.  使用UML来检验比用编码来检验更划算的时候。

      

    3.  有效的使用UML

        1.  使用UML在开发人员之间传达设计概念。

            使用UML创建具体算法对应的图,并不方便。

            UML对创建大型软件结构的“路线图”,比较有用。

            通过UML图可以清楚的发现类与类之间的依赖关系和整个系统的结构。

          

        2.  保留和舍弃

            大多数的UML图都是短命的,应该舍弃的。只需记录在白板或白纸上。

            但下列图应该保存下来:

            1.  表现系统中一个通用设计的解决方案的UML图。

            2.  记录了复杂的协议,难以通过代码了解的UML图。

            3.  提供了较少涉及到系统范围内的“路线图”的UML图。

            4.  比代码更易表述设计意图的UML图。

          

            这些保存下来的UML图,应该经过多次迭代精化修改,最终版本应保存在团队都能访问的公共区域。

            长期保存与临时创建的UML图应该分开存放。

      

        3.  迭代精化

            先研究动态场景,然后确定静态结构的内在含义。

            对动态图(如:协作图)和静态图(如:类图)进行多次迭代并不断完善。

          

      

    4.  最适合创建文档的时机:

        团队完成了所有工作,项目即将结束的时候。

      

    5.  使用Interface实现事件机制,避免了button对Dialler的依赖。

        这样按下按键之后可以拨号,也可以做别的事。

      

    6.  适配器

        适配器也叫转接器,用于在一件或多件仪器的不同部件之间实现有效兼容性的装置。

        适配器的作用:实现接口,转发消息。

      

    7.  画UML图时应时刻想像着如何转化为代码。

  

    8.  UML图的目标并不是要如何正确,而是要讨论它的人都理解它,所以UML图应尽量简洁。

      

    9.  什么时候画UML图:

        1.  多人参与开发,且这些人需要理解一个系统的特定部分的设计结构时,开始画UML图。

            所有人都声明已经理解了的时候,停止画UML图。

          

        2.  两个或多人之间对某个设计点产生意见分歧,进行讨论时,需要画UML图。

            讨论完毕,做出决定后,停止画UML图。

          

        3.  思考一个设计时,画UML图会有所帮助。

            思考成熟并完成相应的代码后,停止画UML图。

          

        4.  向他人或自己解释一段代码的逻辑结构时,开始画UML图。

            发觉看代码能理解的更清楚时,停止画UML图。

          

        5.  当项目快要结束,客户需要UML图和文档时,开始画UML图。

      

    10. 什么时候停止画UML图:

        1.  画图并不是一个必经的过程。

        2.  好的设计者只有在需要的时候才编码和画UML图。

        3.  不要在编码之前的设计阶段,去为创建全面的文档而画UML图,这是浪费时间。

        4.  不要为了其他人编码而去画图。

      

    11. UML Case工具:

        1.  UML Case 工具的弊:

            1.  正版价格贵

          

            2.  需要学习周期

      

        2.  自动绘制UML图的协作系统,应当在手动协作系统不够用的时候才去考虑它。

      

        3.  项目中使用UML Case工具或集成于IDE的UML Case工具,应先进行效能实验,三思而后行。

      

    12. 文档

        必须建立文档,但必须谨慎地创建文档。

        软件文档应该言简意赅

        一个软件文档的价值通常与文档的大小成反比。

        wiki是一个团队中协作编写文档的不错方法。

      

3.  第三章 类图

    1.  类图描绘类本身的信息,以及类之间的关系。

  

    2.  类使用正方形表示

        "-"表示私有(private),"+"表示公有(public),"#"表示受保护(protected)

        Figure UML-5-2-1

       

        Figure UML-5-2-1 对应的代码

        public class Dialler

        {

            private Vector digits;

            int nDigits;

            public void digit(int n);

            protected boolean recordDigit(int n);

        };

      

    3.  关联

        大多数情况下是表示对象实例持有着对其它对象的引用。

        Figure UML-5-3-1

       

        Figure UML-5-3-1 对应的代码

        public class Phone

        {

            private Button itsButtons[15];

        };

      

    4.  多重性

        Figure UML-5-4-1

       

        Figure UML-5-4-1 对应的代码

        public class Phonebook

        {

            private Vector itsPnos;

        };

      

        "*"表示数量非常多,所以PhoneBook的成员变量itsPnos的类型往往使用Vector、List或其它容器。

      

    5.  继承

        约定:为了便于区分,通常用垂直方向的箭头表示继承关系,用水平方向的箭头表示关联。

        在UML中,继承箭头指在基类上。

        Figure UML-5-5-1

       

      

        Figure UML-5-5-1 对应的代码

        public class Employee

        {

            ...

        };

      

        public class SalariedEmployee extends Employee

        {

            ...

        };

      

        虚线继承箭头表示实现一个接口。

        虚线继承箭头指向被实现的接口。

        白板上画虚线耗时,可以马虎一下用实线代替。

        Figure UML-5-5-2

       

        Figure UML-5-5-2 对应的代码

        interface ButtonListener

        {

            ...

        };

  

        public class ButtonDiallerAdapter implements ButtonListener

        {

            ...

        };

      

      

        另外一种表示实现接口的方法:

        Figure UML-5-5-3

       

    6.  在一个UML图中,同时展现所有的方法会引起混乱,所以,只提供一批有代表性的方法会使UML图更清晰。

  

    7.  细节

        细节和修饰符大多数时候并不需要,但是有时候它们是很有用的。

        1.  类的构造型

            类的构造型显示在一对双角括号之间,放在类的名称上访。

          

            Java有两种标准的构造型:

            1.  interface

                interface的所有成员函数都是抽象的

          

            2.  utility(工具类)

                utility的所有成员变量和成员函数都是静态的。

              

            类的构造型可以自己定义,但必须所有阅读UML图的人都明白其含义。

          

        2.  抽象类、抽象方法

            用斜体字或用{abstract}属性,表示一个抽象类或一个抽象方法。

            Figure UML-5-7-2-1

           

            Figure UML-5-7-2-1 对应的代码

            public abstract class Shape

            {

                private  Point itsAnchorPoint;

                public abstract void draw();

            };

          

        3.  属性

            1.  属性不是类的一部分,但可用来代表额外的信息。

                属性可被自定义。

          

            2.  属性的形式:用逗号分隔,由名称、值对组成的列表。

                如:{author=Matin,date=20020429,file=shape.java,private}

          

            3.  一个属性的默认值是true,所以{abstract}等价于{abstract=true}

          

            4.  属性被写在类名称的下方。

          

            5.  一个非正式的约定:在白板上{abstract}可以简写成{A}

          

            6.  一般只使用{abstract}属性。

              

        4.  聚合(Aggregation)

            聚合是关联的一种特殊形式,表示一种整体/部分(whole/part)的关系。

            为了防止混淆,应避免使用聚合

            Figure UML-5-7-4-1

           

            Figure UML-5-7-4-1 对应的代码

            public class Whole

            {

                private Part itsPart;

            };

          

        5.  组合(Composition)

            组合是一种特殊的聚合形式

            组合用的极少

            涉及到一个深度复制的问题。

            Figure UML-5-7-5-1

           

            Figure UML-5-7-5-1 对应的代码

            public class Owner

            {

                private Ward itsWard;

            };

          

        6.  多重性(multipicity)

            对象能够持有其它对象的数组或向量,或者说它们能够持有许多同一类型但不同实例的对象。

          

            多重性的表达式:

            数字        精确的数量,使用数组作为容器。

            *或0..*     0个或0个到无数个,使用Vector作为容器

            0..1        0个或1个,在Java中通常用一个空的引用来实现。

            1..*        1个到无数个,使用Vector作为容器

            3..5        3个到5个,使用数组作容器

            0,2..5,9..* 非法的表达式

          

            Figure UML-5-7-6-1

           

            Figure UML-5-7-6-1 对应的代码

            public class BinaryTreeNode

            {

                private BinaryTreeNode leftNode;

                private BinaryTreeNode rightNode;

            };

          

        7.  关联的构造型

            1.  < >     标准UML标记

                源对象创建了目标对象,然后将目标对象传递给系统的其它对象。

                这种关联可应用于工厂模式。

                Figure UML-5-7-7-1

               

                Figure UML-5-7-7-1 对应的代码

                public class A

                {

                    public B makeB()

                    {

                        return new B();

                    }

                };

              

            2.  < >       标准UML标记

                源对象的成员函数中创建了一个目标对象的实例,把这个实例当做一个本地变量。

                Figure UML-5-7-7-2 同 Figure UML-5-7-7-1 ,图中关联的构造型改为< >

               

                Figure UML-5-7-7-2 对应的代码

                public class A

                {

                    public void f()

                    {

                        B b=new B();

                        //use b

                    }

                };

              

            3.  < >   标准UML标记

                源对象的成员函数把目标对象的实例作为参数使用,源对象不保存目标对象的实例。

                Figure UML-5-7-7-3 同 Figure UML-5-7-7-1 ,图中关联的构造型改为< >

               

                Figure UML-5-7-7-3 对应的代码

                public class A

                {

                    public void f(B b)

                    {

                        //use b

                    } 

                };

          

            4.  < >   非标准UML标记

                源对象传递目标对象的一个成员函数时,用到< >

                < >可应用于多种涉及模式,如:Proxy、DECORATOR和COMPOSITE7。

                Figure UML-5-7-7-4  同 Figure UML-5-7-7-1 ,图中关联的构造型改为< >

              

                Figure UML-5-7-7-4 对应的代码

                public class A

                {

                    private B itsB;

                    public void f()

                    {

                        itsB.f();

                    }

                };

              

        8.  内部类

            Figure UML-5-7-8-1

           

            Figure UML-5-7-8-1 对应的代码

            public class A{

                private class B{

                    ...

                }

            };

          

        9.  匿名内部类

            UML还未对匿名内部类提供官方支持

            一种非官方的表示方法,用有一个< >构造型的嵌入类来表示。

          

            Figure UML-5-7-9-1

           

            Figure UML-5-7-9-1 对应的代码

            public class Window

            {

                public void f()

                {

                    ActionListener l = new ActionListener()

                    {

                        //implementation

                    };

                }

            };

          

          

        10. 关联类

            关联类能进一步的展示一个特殊的关联如何被实现。

            比如:对于多重性关联使用何种容器

          

            Figure UML-5-7-10-1

           

            Figure UML-5-7-10-1 对应的代码

            public class Address

            {

                private Vector itsLines;

            };

          

            关联类能够指明的特定形式的引用有

            1.  不固定的引用(WeakReference )

            2.  松散的引用(SoftReference)

            3.  幻影的引用(PhantomReference)

          

            Java 2 引用类使用指南-学习如何有效地使用 SoftReference、WeakReference 和 PhantomReference

            http://www-900.ibm.com/developerWorks/cn/java/j-refs/index.shtml

          

          

        11. 关联限定符

            源对象通过某种类型的关键字或标记与目标对象实例发生对应关联时,使用关联限定符。

            Figure UML-5-7-11-1

           

            Figure UML-5-7-11-1 对应的代码

            public class LoginServlet

            {

                private String empid;

                public String getName()

                {

                    Employee e = DB.getName(empid);

                    return e.getName();

                }

            };

          

            关联限定符使用较少

          

        12. 少用UML远比多用UML好。

4.  第四章  序列图(sequence diagram)  动态图  P43

    1.  要点:

        1.  不要为每个类,每个方法建立序列图,这样太浪费时间。

      

        2.  序列图应当用于表现对象间的连接而不是具体的算法细节。

      

        3.  序列图不应有大量的对象和消息返回,而应该抓住本质。

      

        4.  尽量不要用序列图去描述每一个小细节。

      

        5.  代码能足够清晰的说明自己时,图是多余的,浪费的。

      

        6.  应该努力是更多的代码能清楚的描述自己,更少的使用图。(即增强代码的可读性)

      

        7.  小序列图比大序列图容易理解,所以更具有实用价值。

      

        8.  高层图比低层图更有用,因为共性比差异多。

      

        9.  白板上的序列图,用于与同事进行表达、交流。

            文档中的序列图,用于捕获核心的突出的系统协作。

  

    2.  相关术语:

        对象、生命线(life lines)、消息、数据标记(data tokens)、时间(time)、活动(Activation)

      

    3.  生命线

        1.  垂立在对象、类或参与者下面的虚线,与时间同方向。代表对象或参与者的生命周期。

      

        2.  区别对象和类的方法:

            矩形框中对象名下方有横线,类名下方没有横线。

          

        3.  参与协作的对象或类被放置在序列图的顶部横向排开。

      

    4.  人样图

        1.  表示匿名参与者,一般作为消息发起的起点和终点。

      

        2.  并非所有的序列图都有人样图,但大部分都有。

      

    5.  消息

        1.  代表一个对象调用另一个对象(或类)的成员函数。

      

        2.  生命线之间的横向箭头,箭头上方是消息名。

      

        3.  消息的参数使用放在箭头下方的数据标记(data tokens)表示,或者放在消息名后面的括号里。

      

    6.  活动(Activation)

        1.  可选,大多数情况下不使用

      

        2.  沿着生命线的竖向长条形小框

  

    7.  返回值

        表示返回值的横向向左的箭头无须标记名字。

      

    8.  表示创建一个对象的方法:

        一个没有标记名字的消息指向一个被创建的对象上。

      

    9.  表示回收一个对象的方法:

        一个对象的生命线终止在一个“X”上。

      

        消息箭头指向这个“X”,表明一个对象被GC回收。

      

    10. 表示“循环”的方法:

        P49 Figure 4-8

        循环表达式的前缀:

        *[while id:=idList.next()]

  

    11. 表示“分支”的方法:

        P49 Figure 4-8

          

    12. 费时间的消息

        1.  不费时间的消息,完全水平的箭头。

      

        2.  费时间的消息,与水平有个夹角的箭头。

      

    13. 异步消息

        1.  同步消息,使用实心箭头表示。

      

        2.  异步消息,使用空心箭头表示,将消息发送后可立即获回控制权。

      

        3.  序列图可以发现异步系统中的竞争条件(Race condition)。

  

    14. 表示“多线程”的方法:

        P54 Figure 4-12

        消息名称前加上线程标记前缀,来显示几个不同的线程控制。

      

    15. 表示“活动对象(active objects)”的方法:

        活动对象使用粗体框来表示

      

    16. 表示“向接口发送消息”的方法:

        1.  给接口命名对象,然后就像使用一般对象一样来使用。

            这里强调对象符合接口,而不是接口的实例化。

            P55 Figure 4-15

          

        2.  虽然明确知道对象的类类型,但仍要表示消息是被发送到一个接口上。

            P55 Figure 4-16

      

5.  第五章 用例图 (Use Case) 静态图 P57

    1.  概念:

        1.  用例是有关动作性(行为性)需求的文本性描述。

      

        2.  一个用例是有关一个系统的行为的一个描述。

            这个描述是从一个用户的角度编写的。

      

        3.  一个用例捕获一个事件的可视化序列。

            这个事件是一个系统对单个用户的激励(stimulus)的响应过程。

          

        4.  用例不描述系统隐藏着的机制,它只描述那些用户可见的事情。

      

  

    2.  要点:

        1.  诀窍是保持用例的简单。

  

        2.  对于不断发生变化的事情,就不需要过早的去捕获细节。

  

        3.  用例是编写出来的,而不是画出来的。

      

        4.  过早地用用例去记录明天就会发生变化的细节并不值得。

            除非这些用例在近几周内会被实现。

            可以先将这些用例的名称记下来列成表格,维护着。当它们快被实现时,再填充细节。

          

        5.  所有的UML图中,用例图最容易混淆,也是最没用的。但系统边界图除外。

          

    3.  Alistair Cockburn的《有效编写用例》

        书中可了解参与者,次要参与者,前置条件,后置条件等用例的其它要素。

      

    4.  系统边界图

        1.  用途:

            对开发人员提供的信息太少,但可作为向客户讲解的材料。

          

        2.  要素:

            1.  长方形表示系统边界。

          

            2.  长方形里面的任何东西都是需要开发的系统的一部分。

          

            3.  长方形外面是与系统交互的参与者(actors)。

          

            4.  参与者是为系统提供激励的系统外实体。

          

            5.  参与者通常是人类用户,也可以是其它系统,甚至是设备,如实时时钟。

          

            6.  长方形里面的用例(带名称的椭圆形)与刺激它的参与者连接,连线上不带箭头。

          

            7.  尽量忽略用例关系,因为用例关系会引起“扩展”还是“泛化”的争论。

          

6.  第六章 面向对象设计(OOD)原则 P61

    1.  学习下面五个设计原则的目的

        用来评估一组UML图或一批代码是否被适当地设计。

  

    2.  适当设计的系统:

        容易被理解、容易被改变、容易被重用。

        表现为没有特别的开发困难,是简单的、扼要的和经济的。

      

    3.  糟糕设计的臭味的不同成分:

        1.  僵化性(Rigidity)

            难以修改。对一个改动,要修改好几处地方。

          

        2.  脆弱性(Fragility)

            对某个部分的修改,会使不相干的部分出问题。

          

        3.  牢固性(immobility)

            很难拆分成能重用的组件。

      

        4.  粘滞性(Viscosity)

            模块之间结合太紧密,编辑、编译和测试都变得很麻烦。

      

        5.  不必要的复杂性(Needless Complexity)

          

      

        6.  不必要的重复(Needless Repetition)

            存在大量的复制粘贴,代码看起来都很相似。

      

        7.  晦涩性(Opacity)

            代码无法清楚的描述自己。

  

    4.  依存关系混乱的代码就像意大利式细面条般

  

    5.  五种设计原则

        1.  单一职责原则(SRP)

            Single Responsibility Principle

            P62

          

            1.  一个类应当只有一个改变的原因

          

            2.  一个类要处理的事情太多,就会散发出脆弱性的臭味。

          

            3.  书中的例子是一个既要计算薪水,又要读写磁盘,还要打印报表的类Employee。

      

        2.  开放-封闭原则(OCP)

            Open-Close Principle

            P64

          

            1.  应当能够改变一个类的周边环境,而无须改变类本身。

          

            2.  经常违反OCP原则的是GUI

                违反OCP的实现,将所有的行为放入一个使用GUI API的类中。

                遵循OCP的系统,将GUI的操纵与数据的操作分离。

              

            3.  书中的举例是一个数据与操作分离的GUI实现

                P65 Figure 6-6

              

            4.  单元测试的self shunt模式

                http://www.objectmentor.com/resources/articles/SelfShunPtrn.pdf

              

            5.  设计遵循OCP原则,就可以改变dialog和model的周边环境到一个测试环境,而无须对dialog和model作任何修改。

          

            6.  使用抽象(指接口和抽象类)是遵循OCP原则的一个关键。

          

            7.  遵循OCP原则的方法:

                通常是在编写实际代码之前,编写简单的单元测试。这个单元测试使用Test-First(测试优先)方法进行编写。

          

        3.  Liskov替换原则(LSP)

            Liskov Substitution Principle

            P77

      

            1.  避免造成派生类的方法非法或退化,一个基类的使用者应当不需要知道这个派生类。

          

            2.  基类的使用者,为了使用它们的派生类,应当无须做特别的处理。

                这里的特别处理指的是使用instanceof或向下转型(DownCast)操作。

              

            3.  违反LSP原则的两种情况:

                1.  当调用一个派生类的成员函数时发生了非法使用,导致不得不抛出异常。

              

                2.  使用一个退化的派生类的方法,退化是指这个方法什么都不实现。

              

            4.  违反LSP问题的解决方法是:

                为派生类另外寻找一个基类,而不是勉强从现有的基类派生。

              

            5.  书中的举例是把志愿者类作为类Employee的派生类所产生的麻烦。

          

        4.  依存关系倒置原则(DIP)

            Dependency Invertion Principle

            P79

          

            1.  用依赖接口和抽象类来替代依赖容易变化的具体类。

          

            2.  遵循DIP原则可以减少变化对系统的影响,减少系统的敏感度。

          

            3.  从抽象类和接口派生出来的具体类比抽象类和接口的改变频繁得多,

                所以宁可依赖抽象(指接口和抽象类),也不要依赖那些容易变化的具体类。

              

                如果要继承一个类,就从一个抽象类继承。

                如果要持有一个类的引用,就从一个抽象的类引用。

                如果要调用一个函数,就从一个抽象函数调用。

              

            4.  容易变化的具体类是:

                1.  那些正在开发的具体的类。

              

                2.  那些容易变化的捕获商业逻辑的类。

              

                3.  Vector类或String类不算容易变化的具体类。

              

            5.  遵循DIP原则的方法:

                为那些容易变化的具体类创建并依赖于接口。

              

      

        5.  接口隔离原则(ISP)

            Interface Separate Principle

            P79

          

            1.  给一个对象的每一个使用者一个接口,这个接口仅有使用者需要的方法。

          

            2.  肥类(Fat Class)

                一个有着成堆方法的类。

              

            3.  肥类引起的麻烦:

                肥类的使用者不使用类的大部分方法,却会因为这些方法的改变而受影响。

              

            4.  遵循ISP原则的方法:

                为肥类的使用者提供一个只包含它们需要的方法的接口。

              

        6.  设计原则的应用方法:

            1.  试图让系统时时刻刻遵循所有的原则是不明智的。  

      

            2.  应用这些原则的最好方法是反应式(reactively)方法:

                当第一次觉察到代码里有一个结构性的问题存在时,

                或当第一次意识到一个模块的改变被其它模块影响时,

                尝试应用这些原则去解决问题。

7.  第七章 dX实践 P83

    1.  何为dX实践?

        1.  dX实践是一套规则,轻量级的开发过程。

        2.  dX实践其实就是XP(极限编程)实践,XP倒过来就是dX。

        3.  dX实践是短周期迭代式处理所有事情。

            所有事情包括需求分析、设计实现、测试和文档。

            短周期(即迭代周期)就是两个星期。

    2.  初始探索

        1.  第一次迭代探索的是需求,一般不需要花费两周的时间。

        2.  客户是负责需求和负责决策的人。

        3.  与客户讨论的内容:

            1.  系统怎样运作,一些必须的功能。

                不用作详细记录,目标是搞清楚系统的整体状况。

            2.  识别出用例(Use Case)

                把用例名记录在索引卡片上,卡片就是User Story。

                卡片上还可记些其它重要信息。

    3.  功能特征评估:

        1.  对User Story评估,计算系数。

        2.  评估方法:

            1.  以一个已知Story的评估结果作为基础,根据新的User Story的难易程度进行评估。

            2.  用“完美编程”的天数作为标准。

                “完美编程”就是不受任何干扰,在极其理想的环境下编程。

        3.  评估之后要对User Story进行合并和拆分。

            一个User Story的天数应控制在1天到4天之间。

            避免高估自己实力,或评估太保守。

        4.  花两到三天的时间,很快地粗糙地实现两、三个比较有趣的User Story。

            目的是验证评估,获得点数和人天的对应关系。

            这里的人天是粗糙实现得到的。保质保量的实现,一般是粗糙实现的3倍。

    4.  探索

        1.  计算初始速度

            初始速度 = (3 乘以 粗糙实现的人天)除以 被粗糙实现的Use Story的总点数。

    5.  计划

        以计算得到的速度作为标准,计算每次迭代周期中需完成的Story。

    6.  发布计划

        1.  典型的一次发布周期,包括六次迭代周期即三个月

            如果团队有五人,一次迭代周期是两周(十个工作日),

            那么一次迭代周期为50人天,一次发布周期为300人天。

        2.  一次发布周期可完成的点数=300人天 × 速度。

        3.  客户挑出点数总数为(300人天×速度)的最重要、最有效的User Story,作为发布计划。

    7.  迭代计划

        1.  一次迭代周期为50人天。

        2.  一次迭代周期可完成的点数=50人天×速度。

        3.  把User Story再细分成任务。

            这里的任务是单个开发人员能够负责的简单任务,以4到10人小时为单位。

            这个细分过程需要客户协助识别重要和次要的用户接口。

        4.  细分结束,开发人员挑选符合自己预算的任务。

            对于剩余任务要进行分摊。

            最后的未决任务要和客户商量是否挪到下一个迭代周期。

            直至所有的任务都分配出去。

    8.  中点

        根据前半段迭代周期中完成任务的点数,对后半段剩下的任务进行调整,取消或增加任务。

    9.  速度反馈

        1.  迭代周期结束的时间不能变更。

        2.  根据实际完成的点数重新计算速度,作为下个迭代周期的依据。

        3.  新速度是上一个迭代周期完成的实际点数。

    10. 将迭代与管理阶段联系起来

        1.  “统一过程”项目的四个管理阶段:

            1.  初始阶段

                确定系统的可行性和商业案例。

            2.  细化阶段

                确定系统架构并创建一个比较可靠的实现计划。

            3.  构造阶段

                开始进行系统实现。

            4.  移交阶段

                安装系统,和用户一起测试(UAT测试)。

    11. 一个迭代周期中包括了:

        1.  分析需求

        2.  设计解决方案

        3.  实现解决方案

            只考虑当前迭代期间需要处理的User Story。

    12. 结对开发

        两个开发人员用同一台机器工作,一起处理他们负责的任务。

    13. 可验收测试

        1.  每个迭代周期开始时,客户和QA一起把User Story充实到Use Case,并为之编写可执行的验收测试案例。

        2.  对于程序员验收测试案例就是需求文档。

        3.  整个迭代周期中,程序员持续的运行测试案例,以保证能正确的通过验收测试。

    14. 单元测试

        1.  代码未动,测试先行。

        2.  如果有任何一个单元测试没有通过,就不能编写新的产品代码。

        3.  极限迭代的过程:

            1.  先写一段5到10行的单元测试代码。

            2.  然后为了使这段单元测试代码能够编译通过,开始编写并不断充实产品代码。

            3.  一旦单元测试通过之后,就可以添加新的代码,开始新的迭代。

            4.  一个测试周期通常是1到10分钟。

        4.  极限迭代的目的:

            无论现在进展到什么程度,几分钟前的产品代码总是可以运行的。

        5.  单元测试也是文档的一种形式。

        6.  单元测试代码提供的信息有:

            1.  如何去调用一个特定的API。

            2.  如何创建一个特定的对象。

        7.  这种形式的文档是明确的,准确的,可编译的和可执行的。

    15. 重构:

        1.  只要用大量的单元测试和验收测试作靠山,就可以放心大胆的去修改那些需要修改的任何部分。

        2.  在不改变程序的行为的前提下,改进程序的内部结构就是重构。

        3.  每小时左右的编程结束后,就是几分钟小碎步式的重构,然后测试。

        4.  绝不能把烂代码留到第二天。

    16. 开放式办公环境:

        1.  成员之间有着非常频繁的交流和沟通,可以快速的互相提问,得到最快的响应和建议。

            做到互相依靠,互看代码。

            方便结对编程。

    17. 持续集成:

        1.  check in的规则:

            只有通过单元测试和验收测试的代码才能check in。

        2.  持续性的集成可以避免项目进度到了尾声才来一个非常庞大的终极集成。

    18. 文档:

        1.  Martin文档第一要律:

            只编写那些真正需要的有意义的文档。

        2.  不需要专门用类图来捕获所有的需求。

        3.  不需要在序列图里捕获所有的Use Case。

        4.  只有真正需要使用这些工具的时候才使用,否则就让它们一边歇着去。

8. 第八章 包(Packages)P92

    1.  Java的两类重要的包:

        1.  源代码包。package

        2.  二进制组件。.jar

    2.  Java Packages

        1.  Java Packages是名称空间(Name Spaces)

            它们允许程序员创建小的私有空间,以便在其中声明类。

            这些类的名字不会与其它包中同名的类发生冲突。

        2.  UML中表示一个包的几种方式:

            1.  包的图标是一个矩形框,在其顶部有一个标签,像一个文件夹。

                完整的包名显示在矩形框中间。

                P92 Figure 8-1

            2.  也可以将完整的包名写在矩形框上的标签中。

                剩下大的矩形框中列出包中定义的所有类。

                P92 Figure 8-2

            3.  可以使用包含关系(contains)显示包的嵌套结构。

                P93 Figure 8-3

        3.  依赖(Dependencies)

            在同一个包中的代码常常依赖另一个包中的代码。

            在UML中用依赖关系(dependency)表示这种依赖性。

            P93 Figure 8-4

            import不会创建真正的依赖关系,只有调用函数才会创建依赖关系。

    3.  二进制组件.jar文件(Binary Components)

        1.  UML中表示一个.jar组件的方式

            P94 Figure 8-5

        2.  一个组件常常包含一个或多个包,所以组件之间的依赖关系通常是包之间依赖关系的子集。

    4.  包设计的原则(Principles of Package Design)

        1.  遵循原则的作用:

            1.  易变的类放在一起。

            2.  将因为各种原因需要被改变的类分离开。

            3.  将经常改变的类和不常变化的类独立开。

            4.  分离系统的高层结构和底层实现细节。

        2.  发布/重用等价原则(The Release/Reuse Equivalency Principle)(REP)

            1.  被重用的一组类应该放到一个包中。然后这个包被那些重用它们的开发人员发布和跟踪。

            2.  创建一个包是为了方便别人重用。

            3.  重用的粒度就是发布的粒度。

                重用的最小的东西,是值得别人去发布和跟踪的。

        3.  公共闭合原则(The Common Closure Principle)(CCP)

            1.  CCP相当于OOD的单一责任原则(SRP)

            2.  由于相同原因要被修改的类放在一个包中。

        4.  公共重用原则(The Common Reuse Principle)(CRP)

            1.  CRP相当于OOD的接口隔离原则(ISP)

            2.  应该尽可能地将只被一个客户使用的包与被多个不同客户使用的包分开。

            3.  避免对包中某个类的修改会影响到其它没有使用该类的客户。

        5.  非循环依赖原则(The Acyclic Dependencies Principle)(ADP)

            1.  包的循环依赖将导致编译和开发方面的问题。

            2.  应避免在依赖图中出现循环依赖。

            3.  可以使用JDepend等工具,来检查是否存在循环依赖。

        6.  稳定依赖原则(The Stable Dependencies Principle)(SDP)

            1.  包不应该依赖那些比它们自己更不稳定(更易改变)的包。

            2.  每个包所依赖的包应当比依赖它们的包更稳定。

            3.  如果一个包有多个指向它的依赖,而它自己又依赖一个易发生改变的包,其结果就导致这个包很难改变。

        7.  稳定抽象原则(The Stable Abstractions Principle)(SAP)

            1.  SAP相当于OOD的开放-封闭原则(OCP)

            2.  为了保证稳定的包易被控制,稳定的包应该是抽象的。

                越稳定的包应该越抽象。

                抽象类和接口的比例越大的包就越抽象。

            3.  SDP原则和SAP原则相结合就成了包版本的依存关系倒置(DIP)原则。

            4.  结合SDP原则和SAP原则可得出:

                1.  稳定性随指向它的依赖的增多而增加。

                2.  抽象性应该随稳定性的增加而增加。

                3.  所以,抽象性应该随指向它们的依赖的增多而增加。

            5.  包依赖图对于解决依赖循环问题和判断编译顺序都很有帮助。

9. 第九章  对象图(Object Diagrams) P98

    1.  UML对象图就像系统运行时的一个快照。

    2.  对象图包含的信息有:

        1.  系统在某个特定时刻,或者系统处于某个特定状态时的内部结构。

        2.  对象和关系被实际使用的方式。

        3.  系统根据不同输入的变化情况。

    3.  对象图被使用的情况很少。

    4.  对象图所用的标记与类图相同。

    5.  对象图的另一个有用之处是在多线程系统中。

        1.  主动对象(Active Object)

            用粗线条框表示。

        2.  主动对象就像是线程的控制者。

            它们包含有控制线程的方法,如:Start、Stop、SetPrioring等。

            P102 Figure 9-4

    6.  在多线程系统中,对象图比类图更具有表达力的原因:

        这段程序结构是在运行时建立的,这是一种关于对象的结构而不是关于类的结构。

    7.  很多对象图能从类图直接推断出来,所以对象图应用的比较少。

10. 第十章  状态图(Stat Diagrams)P103

    1.  状态转换图(State Transition Diagrams)(STD)

        P103 Figure 10-1

        1.  状态

            使用圆形拐角矩形表示。

        2.  状态的名字

            放在圆形拐角矩形上面的框格中。

        3.  进入或退出状态时触发的动作。

            放在圆形拐角矩形下面的框格中。

        4.  状态之间的转换

            使用箭头表示,箭头指向表示从原状态到目标状态。

        5.  状态转换箭头上标记:

            1.  触发转换的每一个事件的名称。

            2.  当转换被触发时执行的动作。

        6.  初始伪状态(Initial Pseudo State)

            使用实心的黑色圆圈表示。

            标明了有限状态机生命周期的起始状态。

        7.  超状态(Super State)

            超状态适用于有多个状态要用相同的方式响应某些相同事件的情况。

            画一个超状态把那些类似的状态括起来,然后只需从超状态画一个状态转换箭头,

            来代替从每一个状态画转换箭头。

            超状态的转换可以被子状态的转换重载。

            P106 Figure 10-5

            超状态和一般的状态一样有entry、exit和专用事件。

            注意:超状态和其子状态的事件调用顺序。

            P106 Figure 10-6

        8.  专用事件

            表示状态的圆形拐角矩形的下面框格中包含了多对事件和动作。

            事件和动作的书写形式:event/action

            两个标准的事件是entry和exit。

            可以自定义事件。

        9.  初始伪状态和结束伪状态。

            初始伪状态不需要有事件,但可以有一个动作。

            这个动作是在有限状态机被创建以后,第一个被调用的动作。

            转换到结束伪状态时的动作是有限状态机最后执行的一个动作。

        10. 有限状态机图的使用:

            对于日趋完善中(即频繁变更中)的系统,

            在文档中创建STTS(State Transition Tables)要好于使用STDS(State Transition Diagrams)。

            在开发和维护有限状态机方面,使用文本语言比图形更加简单。

 

 

--------------------------------------------------------------------------------

版权所有:UML软件工程组织 

女儿实际还是讲道理的

  从小到现在,女儿两岁半了,大家一直都认为女儿的性格急,不大讲理。实际这是我们的误解,只要我们用正确的方法引导她,女儿还是挺讲道理的。

  如:

  和她一起上街玩,她要吃的东西,给她买了。对她认真说,这个吃完了今天就不买了。她也认真的点了点头。她吃完后,就真的不提再买吃的了。

  又,我要去上班了。问她:娃娃,现在几点了?爸爸是不是该上班了?她就说,是该爸爸上班了,点点头,然后跟我说再见。如果我们不讲方式方法,直接去上班,她不少时候就会哭闹,不许我们去上班或者要一起去。

  这时,我又想起上次听一位幼儿教育工作者的讲演,要树立孩子讲信用的习惯,要说话算数。

  另外,孩子不对的要求绝对不要答应,更不要她一哭就答应了。不然她认为哭就是手段了。这方面我自认为比她的婆婆爷爷和妈妈做的好。也许是我的理性要重一些吧。

(转帖)基于web2.0的网站创意

(转自:http://www.matrix.org.cn/resource/article/44/44089_web2.0.html)

基于web2.0的网站创意

cleverpig 发表于 2005-12-29

点击数:43 评论数:1 评价:0/0

关键词:网站创意,web2.0,blog

摘要:

本人是在一个偶然的机会看到这篇文章,文中对基于web2.0的网站创意进行了大胆的设想:使用blogger组将所有的blogger团结在一起互相交流,但并非RSS聚合、Trackback那样来源单一。引用一下原作者的话:“这个“组”则是建立这个“组”的人一开始就定义好了“组”的主题。并且建立一个“组”相当于建立起了一个网站,用经营网站的模式去经营这个“组”将会给建“组”的人和加入这个“组”的人带来收益。”。 文章工具

收藏

投票评分

发表评论

复制链接

内容摘要

本人是在一个偶然的机会看到这篇文章,文中对基于web2.0的网站创意进行了大胆的设想:使用blogger组将所有的blogger团结在一起互相交流,但并非RSS聚合、Trackback那样来源单一。引用一下原作者的话:“这个“组”则是建立这个“组”的人一开始就定义好了“组”的主题。并且建立一个“组”相当于建立起了一个网站,用经营网站的模式去经营这个“组”将会给建“组”的人和加入这个“组”的人带来收益。”。

原文如下:

一、基于web2.0的网站创意

请先看这个图图(画得不错吧,哈哈~),在上过很多的BLOG之后,我发现BLOGGER在写日志时会因为BLOGGER的兴趣、生活状态、客观现实对他的影响而产生日志主题的随意性。那么为什么不可以把这些随意性很大的日志重新排列组合呢?所以,我产生出这个“组”的概念。

这个“组”的功能与Flickr中Groups的运行方式是差不多的,当BLOGGER写好一个日志后,他可以到组里去,把他写的日志的引用地址,向组中提交,从而在组中把这个日志显示出来~

有些与tag类似但又不同,相对于tag是浏览者对所看日志进行定义,这个“组”则是建立这个“组”的人一开始就定义好了“组”的主题。并且建立一个“组”相当于建立起了一个网站,用经营网站的模式去经营这个“组”将会给建“组”的人和加入这个“组”的人带来收益。

“组”还与RSS在线阅读(如:Bloglines)类似但又不同,RSS在线阅读是订阅了全部BLOG中的内容,前面说过的写BLOG的人随意性很大,会写出我们不感兴趣的内容,通过这个组,因为写BLOG的人已经根据自己的文章内容进行了分类,所以不会出现我们不感兴趣的内容。并且因为是相关主题的日志是在一起的,所以可以让看的人看到不同方面的观点,集思广忆。

“组”还与trackback类似但又不同,BLOG向“组”发送日志就是基于与trackback相同的技术:XML(具体可能要用到XMLHTTP与XMLDOM)。便不同在与trackback是两个BLOG之间的交流,而“组”将是N个BLOG之间的。

“组”还与RSS聚合网站(如:BXNA)类似但又不同,RSS聚合网是自动聚合各个分开的BLOG内容的,而“组”则是BLOGGER自己去把自己认为合适的日志加到合适的“组”里去。但也有一些问题,就是有些麻烦,如果BLOG程式中可以加入这个“组”的选择就好了。

也许的也许,这会是一场革命!谁说不是呢?什么事都可能发生!

二、为什么这个创意可行?

通过对 《05年中国5城市互联网使用现状及影响调查报告》和 《網誌使用者與使用行為之研究》的研究报告分析我发现,上网者对网站内容的“有用性”和“易用性”很重视,当一个网站内容“有用”而且“易用”时,会产生一种“知覺愉悅”,那么这个“组”会最大限度的提高“组”内内容的“有用”性!但“组”的“易用性”还需要在设计这个系统时进行认真考虑!

在两个报告中都显示出,网龄比较长的用户喜欢使用BLOG这种模式,并且BLOGGER的飞速增长也是不可争事实,那么这么多的BLOG而产生大量的信息,通过对如何进行利用的分析可知“组”是比较合理的一种信息管理方式。

而且年輕、高學歷、且具有長期網路使用經驗與網頁製作經驗的人群是BLOGGER的主体人群,高学历,高智商会给他们代来高收入,那么针对这样的人群开发相关产品会代来很好的收益。

现有的技术发展也正好可以实现这种模式,基于XML相关的各种技术也在不断的开发出来,如AJAX,RIA,WEB2.0,网站重构,乃至IE7.0也要加入支持RSS的功能,基于XML相关技术的产品是很有发展潜力的。

三、实现创意需要什么?

创意很简单,也要需要精心分析和策划,特别是如果让本系统让BLOGGER和浏览者产生“知覺愉悅”将是一个要非常小心的环节,这需要一位非常厉害的交互设计师(注意:不是UI设计师)来全局把握!

当然还需要对XML应用技术非常了解的技术人员~

还有对本系统进行投资模型、设入成本预案、市场规划、启动计划、发展计划的商业规划~

四、他会产生什么后果?

他会对世界互联网的格局发生根本性转变

五、关键技术解答

为什么他是web2.0的?

因为他是集Blog、TrackBack、RSS、Wiki、Tag、六度分隔理论于一体的,本系统是站在以上技术的之上的,因为站在巨人的肩膀上,所以看得更远!

六、创意来源

以后再说。。。让我放松几天,这几天想得头都大了~

本页面地址:  

→投票评分

     非常好 还行 一般 扔鸡蛋          总得分:0 / 投票人次:0

→用户评论列表

#7314 评论作者: littlebat 发表时间:2005-12-29 06:05

我要认真读一下这篇文章,我发现我在学习日记上的创意可以上升到作者的创意高度。