`
jimode2013
  • 浏览: 37618 次
社区版块
存档分类
最新评论

thinking in java中出现的设计模式代码例子

 
阅读更多

代理模式

// : typeinf0/SimpleProxyDemo.j ava 
import static net.mindview.util.Print.*;

interface Interface {
	void doSomething();

	void somethingElse(String arg);
}

class RealObject implements Interface {
	public void doSomething() {
		print("doSomething");
	}

	public void somethingElse(String arg) {
		print("somethingElse " + arg);
	}
}

class SimpleProxy implements Interface {
	private Interface proxied;

	public SimpleProxy(Interface proxied) {
		this.proxied = proxied;
	}

	public void doSomething() {
		print("SimpleProxy doSomething");
		proxied.doSomething();
	}

	public void somethingElse(String arg) {
		print("SimpleProxy somethingElse " + arg);
		proxied.somethingElse(arg);
	}
}

public class SimpleProxyDemo {
	public static void consumer(Interface iface) {
		iface.doSomething();
		iface.somethingElse("bonobo");
	}

	public static void main(String[] args) {
		consumer(new RealObject());
		consumer(new SimpleProxy(new RealObject()));
	}
}

 动态代理模式

// : typeinfo/SimpleDynamicProxy.java 
import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
	private Object proxied;

	public DynamicProxyHandler(Object proxied) {
		this.proxied = proxied;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("**** proxy: " + proxy.getClass() + ", method: "
				+ method + ", args: " + args);
		if (args != null)
			for (Object arg : args)
				System.out.println(" " + arg);
		return method.invoke(proxied, args);
	}
}

public class SimpleDynamicProxy {
	public static void consumer(Interface iface) {
		iface.doSomething();
		iface.somethingElse("bonobo");
	}

	public static void main(String[] args) {
		RealObject real = new RealObject();
		consumer(real);
		// Insert a proxy and call again:
		Interface proxy = (Interface) Proxy.newProxyInstance(
				Interface.class.getClassLoader(),
				new Class[] { Interface.class }, new DynamicProxyHandler(real));
		consumer(proxy);
	}
}

 

//: typeinfo/SelectingMethods.java 
// Looking for particular methods in a dynamic proxy. 
import java.lang.reflect.*;
import static net.mindview.util.Print.*;

class MethodSelector implements InvocationHandler {
	private Object proxied;

	public MethodSelector(Object proxied) {
		this.proxied = proxied;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if (method.getName().equals("interesting"))
			print("Proxy detected the interesting method");
		return method.invoke(proxied, args);
	}
}

interface SomeMethods {
	void boringl();

	void boring2();

	void interesting(String arg);

	void boring3();
}

class Implementation implements SomeMethods {
	public void boringl() {
		print("boringl");
	}

	public void boring2() {
		print("boring2");
	}

	public void interesting(String arg) {
		print("interesting " + arg);
	}

	public void boring3() {
		print("boring3");
	}
}

public class SelectingMethods {
	public static void main(String[] args) {
		SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(
				SomeMethods.class.getClassLoader(),
				new Class[] { SomeMethods.class }, new MethodSelector(
						new Implementation()));
		proxy.boringl();
		proxy.boring2();
		proxy.interesting("bonobo");
		proxy.boring3();
	}
}

 Strategy pattern

a special case of the Strategy pattern

Null Objects

The problem is that null has no behavior of its own except for producing a NullPointerException if you try to do anything with it. Sometimes it is useful to introduce the idea of a Null Objects that will accept messages for the object that it's "standing in" for, but will return values indicating that no "real" object is actually there. This way, you can assume that all objects are valid and you don't have to waste programming time checking for null (and reading the resulting code). 

Although it's fun to imagine a programming language that would automatically create Null Objects for you(Object C就是这样的), in practice it doesn't make sense to use them everywhere—sometimes checking for null is fine, and sometimes you can reasonably assume that you won't encounter null, and sometimes even detecting aberrations via NullPointerException is acceptable. The place where Null Objects seem to be most useful is "closer to the data," with objects that represent entities in the problem space. As a simple example,many systems will have a Person class, and there are situations in the code 

where you don't have an actual person (or you do, but you don't have all the information about that person yet), so traditionally you'd use a null reference and test for it. Instead, we can make a Null Object. But even though the Null Object will respond to all messages that the "real" object will respond to, you still need a way to test for nullness. The simplest way to do this is to create a tagging interface: 

 

// : net/mindview/uti1/Null.java 
package net.mindview.util ; 
public interface Null { }

 This allows instanceof to detect the Null Object, and more importantly,does not require you to add an isNull( ) method to all your classes (which would be, after all, just a different way of performing RTTI—why not use the built-in facility instead?). 

 

//: typeinfo/Person.java 
// A class with a Null Object. 
import net.mindview.util.*;

class Person {
	public final String first;
	public final String last;
	public final String address;

	// etc.
	
	
	public Person(String first, String last, String address) {
		this.first = first;
		this.last = last;
		this.address = address;
	}

	public String toString() {
		return "Person: " + first + " " + last + " " + address;
	}

	public static class NullPerson extends Person implements Null {
		private NullPerson() {
			super("None", "None", "None");
		}

		public String toString() {
			return "NullPerson";
		}
	}

	public static final Person NULL = new NullPerson();
} // /:-

In general, the Null Object will be a Singleton, so here it is created as a static final instance. This works because Person is immutable—you can only set the values in the constructor, and then read those values, but you can't modify them (because Strings themselves are inherently immutable). If you want to change a NullPerson, you can only replace it with a new Person object. Notice that you have the option of detecting the generic Null or the more specific NullPerson using instanceof, but with the Singleton approach you can also just use equals( ) or even == to compare to Person.NULL. 

Now suppose you're back in the high-flying days of Internet startups and you've been given a big pile of venture funding for your Amazing Idea. You'r ready to staff up, but while you're waiting for positions to be filled, you can use Person Null Objects as placeholders for each Position: 

//: typeinfo/Position.java 
public class Position {
	private String title;
	private Person person;

	public Position(String jobTitle, Person employee) {
		title = jobTitle;
		person = employee;
		if (person == null)
			person = Person.NULL;
	}

	public Position(String jobTitle) {
		title = jobTitle;
		person = Person.NULL;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String newTitle) {
		title = newTitle;
	}

	public Person getPerson() {
		return person;
	}

	public void setPerson(Person newPerson) {
		person = newPerson;
		if (person == null)
			person = Person.NULL;
	}

	public String toString() {
		return "Position: " + title + " " + person;
	}
} // /:-

With Position, we don't need to make a Null Object because the existence of Person.NULL implies a null Position (it's possible that, later, you'll discover the need to add an explicit Null Object for Position, but YAGNI(You Aren't Going to Need It) says to try "the simplest thing that could possibly work" for your first draft, and to wait until some aspect of the program requires you to add in the extra feature, rather than assuming it's necessary). 

The Staff class can now look for Null Objects when you are filling positions:

// : typeinfo/Staff.java 
import java.util.*;

public class Staff extends ArrayList<Position> {
	public void add(String title, Person person) {
		add(new Position(title, person));
	}

	public void add(String... titles) {
		for (String title : titles)
			add(new Position(title));
	}

	public Staff(String... titles) {
		add(titles);
	}

	public boolean positionAvailable(String title) {
		for (Position position : this)
			if (position.getTitle().equals(title)
					&& position.getPerson() == Person.NULL)
				return true;
		return false;
	}

	public void fillPosition(String title, Person hire) {
		for (Position position : this)
			if (position.getTitle().equals(title)
					&& position.getPerson() == Person.NULL) {
				position.setPerson(hire);
				return;
			}
		throw new RuntimeException("Position " + title + " not available");
	}

	public static void main(String[] args) {
		Staff staff = new Staff("President", "CTO", "Marketing Manager",
				"Product Manager", "Project Lead", "Software Engineer",
				"Software Engineer", "Software Engineer", "Software Engineer",
				"Test Engineer", "Technical Writer");
		staff.fillPosition("President", new Person("Me", "Last",
				"The Top, Lonely At"));
		staff.fillPosition("Project Lead", new Person("Janet", "Planner",
				"The Burbs"));
		if (staff.positionAvailable("Software Engineer"))
			staff.fillPosition("Software Engineer", new Person("Bob", "Coder",
					"Bright Light City"));
		System.out.println(staff);
	}
}

 Notice that you must still test for Null Objects in some places, which is not that different from checking for null, but in other places (such as toString( ) conversions, in this case), you don't have to perform extra tests;you can just assume that all object references are valid. 

If you are working with interfaces instead of concrete classes, it's possible to use a DynamicProxy to automatically create the Null Objects. Suppose we have a Robot interface that defines a name, model, and a List < Operation > that describes what the Robot is capable of doing. Operation contains a description and a command (it's a type of Command pattern): 

//: typeinfo/Operation.Java 
public interface Operation {
	String description();

	void command();
} // /:-

 You can access a Robot's services by calling operations( ):

// : typeinfo/Robot.java 
import java.util.*;
import net.mindview.util.*;

public interface Robot {
	String name();

	String model();

	List<Operation> operations();

	class Test {
		public static void test(Robot r) {
			if (r instanceof Null)
				System.out.println(" [Null Robot] ");
			System.out.println("Robot name: " + r.name());
			System.out.println("Robot model: " + r.model());
			for (Operation operation : r.operations()) {
				System.out.println(operation.description());
				operation.command();
			}
		}
	}
} // /:-

This also incorporates a nested class to perform tests. 

We can now create a Robot that removes snow: 

 

// : typeinfo/SnowRemovalRobot.Java 
import java.util.*;

public class SnowRemovalRobot implements Robot {
	private String name;

	public SnowRemovalRobot(String name) {
		this.name = name;
	}

	public String name() {
		return name;
	}

	public String model() {
		return "SnowBot Series 11";
	}

	public List<Operation> operations() {
		return Arrays.asList(new Operation() {
			public String description() {
				return name + " can shovel snow";
			}

			public void command() {
				System.out.println(name + " shoveling snow");
			}
		}, new Operation() {
			public String description() {
				return name + " can chip ice";
			}

			public void command() {
				System.out.println(name + " chipping ice");
			}
		}, new Operation() {
			public String description() {
				return name + " can clear the roof";
			}

			public void command() {
				System.out.println(name + " clearing roof");
			}
		});
	}

	public static void main(String[] args) {
		Robot.Test.test(new SnowRemovalRobot("Slusher"));
	}
}
There will presumably be many different types of Robot, and we'd like to have each Null Object do something special for each Robot type—in this case, incorporate information about the exact type of Robot the Null Object is standing for. This information will be captured by the dynamic proxy: 
//: typeinfo/NullRobot.java 
// Using a dynamic proxy to create a Null Object. 
import java.lang.reflect.*;
import java.util.*;
import net.mindview.util.*;

class NullRobotProxyHandler implements InvocationHandler {
	private String nullName;
	private Robot proxied = new NRobot();

	NullRobotProxyHandler(Class<? extends Robot> type) {
		nullName = type.getSimpleName() + " NullRobot";
	}

	private class NRobot implements Null, Robot {
		public String name() {
			return nullName;
		}

		public String model() {
			return nullName;
		}

		public List<Operation> operations() {
			return Collections.emptyList();
		}
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		return method.invoke(proxied, args);
	}
}

public class NullRobot {
	public static Robot newNullRobot(Class<? extends Robot> type) {
		return (Robot) Proxy.newProxyInstance(NullRobot.class.getClassLoader(),
				new Class[] { Null.class, Robot.class },
				new NullRobotProxyHandler(type));
	}

	public static void main(String[] args) {
		Robot[] bots = { new SnowRemovalRobot("SnowBee"),
				newNullRobot(SnowRemovalRobot.class) };
		for (Robot bot : bots)
			Robot.Test.test(bot);
	}
}
 Whenever you need a null Robot object, you just call newNullRobot( ),passing the type of Robot you want a proxy for. The proxy fulfills the requirements of the Robot and Null interfaces, and provides the specific name of the type that it proxies. 

Factory Method design pattern

 

 

// : generics/coffee/Coffee.Java 
package generics.coffee;

public class Coffee {
	private static long counter = 0;
	private final long id = counter++;

	public String toString() {
		return getClass().getSimpleName() + " " + id;
	}
} // /: -

 

//: generics/coffee/Latte.java
package generics.coffee; 
public class Latte extends Coffee {}

 

//: generics/coffee/Mocha.Java 
package generics.coffee; 
public class Mocha extends Coffee {} ///:-

 

 

 

 

//: generics/coffee/Cappuccino.java 
package generics.coffee; 
public class Cappuccino extends Coffee {} ///:-

 

 

 

 

//: generics/coffee/Americano.Java 
package generics.coffee; 
public class Americano extends Coffee {} 

 

//: generics/coffee/Breve.Java 
package generics.coffee; 
public class Breve extends Coffee {}

 

 

Now we can implement a Generator < Coffee > that produces random different types of Coffee objects: 

 

//: generics/coffee/CoffeeGenerator.java 
// Generate different types of Coffee: 
package generics.coffee;

import java.util.*;
import net.mindview.util.*;

public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
	private Class[] types = { Latte.class, Mocha.class, Cappuccino.class,
			Americano.class, Breve.class, };
	private static Random rand = new Random(47);

	public CoffeeGenerator() {
	}

	// For iteration:
	private int size = 0;

	public CoffeeGenerator(int sz) {
		size = sz;
	}

	public Coffee next() {
		try {
			return (Coffee) types[rand.nextInt(types.length)].newInstance();
			// Report programmer errors at run time:
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	class Coffeelterator implements Iterator<Coffee> {
		int count = size;

		public boolean hasNext() {
			return count > 0;
		}

		public Coffee next() {
			count--;
			return CoffeeGenerator.this.next();
		}

		public void remove() { // Not implemented
			throw new UnsupportedOperationException();
		}
	};

	public Iterator<Coffee> iterator() {
		return new Coffeelterator();
	}

	public static void main(String[] args) {
		CoffeeGenerator gen = new CoffeeGenerator();
		for (int i = 0; i < 5; i++)
			System.out.println(gen.next());
		for (Coffee c : new CoffeeGenerator(5))
			System.out.println(c);
	}
}

 

Here's a second implementation of Generator<T>, this time to produce Fibonacci numbers: 

package generics.coffee;

//: generics/Fibonacci.java 
// Generate a Fibonacci sequence, 
import net.mindview.util.*;

public class Fibonacci implements Generator<Integer> {
	private int count = 0;

	public Integer next() {
		return fib(count++);
	}

	private int fib(int n) {
		if (n < 2)
			return 1;
		return fib(n - 2) + fib(n - 1);
	}

	public static void main(String[] args) {
		Fibonacci gen = new Fibonacci();
		for (int i = 0; i < 18; i++)
			System.out.print(gen.next() + " ");
	}
} 

 

Adapters 

原本的实现:

package generics.coffee;

//: generics/Fibonacci.java 
// Generate a Fibonacci sequence, 
import net.mindview.util.*;

public class Fibonacci implements Generator<Integer> {
	private int count = 0;

	public Integer next() {
		return fib(count++);
	}

	private int fib(int n) {
		if (n < 2)
			return 1;
		return fib(n - 2) + fib(n - 1);
	}

	public static void main(String[] args) {
		Fibonacci gen = new Fibonacci();
		for (int i = 0; i < 18; i++)
			System.out.print(gen.next() + " ");
	}
} 

 

package generics.coffee;

//: generics/IterableFibonacci.java 
// Adapt the Fibonacci class to make it Iterable. 
import java.util.*;

public class IterableFibonacci extends Fibonacci implements Iterable<Integer> {
	private int n;

	public IterableFibonacci(int count) {
		n = count;
	}

	public Iterator<Integer> iterator() {
		return new Iterator<Integer>() {
			public boolean hasNext() {
				return n > 0;
			}

			public Integer next() {
				n--;
				return IterableFibonacci.this.next();
			}

			public void remove() { // Not implemented
				throw new UnsupportedOperationException();
			}
		};
	}

	public static void main(String[] args) {
		for (int i : new IterableFibonacci(18))
			System.out.print(i + " ");
	}
}

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics