So GREAT The new version of Effective Java(2nd edition) is! (Better than Effective c# 1 million times)
I’m reading this book and finding that lots of the skill can be used by other language programmers, especially c#’s.
This series of notes will cover the the things for both java and c#.
Wish you like it.
Classes and Interfaces
- Item 13: Minimize the accessibility of classes and members
All you can use the term : encapsulation.
The basic principles of object-oriented programming are encapsulation, modularity, polymorphism, and inheritance.
Let me compare the modifiers of Java and c# first.
Java c#
private private(default)
package-private(default) internal
protected protected
public public
(No this modifier) protected internal
Both of they have the package-private (internal in c#) modifier, this is new to c++ programmer (so is “finally” for exception process). It’s very useful when you wanna write test case for it.
No one wanna test a private method, even we can do so by some reflection-tool. So you can make it package-private (or internal).
“To facilitate testing, you may be tempted to make a class, interface, or member more accessible. This is fine up to a point. It is acceptable to make a private member of a public class package-private in order to test it, but it is not acceptable to raise the accessibility any higher than that. In other words, it is not acceptable to make a class, interface, or member a part of a package’s exported API to facilitate testing. Luckily, it isn’t necessary either, as tests can be made to run as part of the package being tested, thus gaining access to its package-private elements.” – Effective Java 2ed
It means you should put your test code at the same package.
But for c# programmer, there’s a better choice: authorize your test assembly to visit the internal elements. It’s easy, just use the InternalsVisibleToAttribute.
I’ve used this trick in the Google APIs for .NET project.
You should always consider the interface-oriented design. Let the user get an instance of the interface (with Dependency Inject or Abstract Factory) but not know its true type. It will make your design more clear.
- Item 14: In public classes, use accessor methods, not public fields
Indeed, I think you should always this rule even for non-public classes.
c# programmer can use properties, it looks more natural.
You don’t need to worry about the performance, the smart modern compiler will do all the things for you.
- Item 15: Minimize mutability
It seems the same, item 7 in Effective c#: Item 7: Prefer Immutable Atomic Value Types.
Most of time, you can make your information class immutable.
It’s thread security and you can cache it.
The disadvantage is the performance problem. You need to create a new instance every time.
You can use some helper class, like the StringBuilder for String.
You can also create a mutable subclass in your package for some operate. Like the transition for a matrix.
Don’t forget to check if it the “real” immutable object.
public static BigInteger safeInstance(BigInteger val) {
if (val.getClass() != BigInteger.class)
return new BigInteger(val.toByteArray());
return val;
}
“If a class cannot be made immutable, limit its mutability as much as possible” – Effective Java 2ed
- Item 16: Favor composition over inheritance
“Unlike method invocation, inheritance violates encapsulation” – Effective Java 2ed
So if we don’t use inheritance, what should we do? The composition.
It’s just some wrapper classes, something like the smart pointer if you familiar with c++.
// Wrapper class - uses composition in place of inheritance
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
One principle in Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin is that, one class inherit from its super only if it can replace it super completely. It not just means the “is-a” relationship, it means sub can do everything its super can. It means their functions. The square and rectangle is a good example, square is a rectangle in math, but you’d better let them inherit from one base class (You can read the book for more detail about this example and other things).
Writing a wrapper class is tedious in Java and c# (but easy in some dynamic language like ruby etc.), but it very useful.
One thing I like most is that it can implement multiple inheritance in Java and c# by the Decorator pattern.
Don’t be afraid of the inheritance after read this article. Some times, inheritance can make your life easier.
One case in the ASP.NET 2.0 Website Programming: Problem – Design – Solution said that, all the page classes are inherited from an abstract utility class. You can make the utility class be a static utility class. But the author of this book think this is the best way in practice. You can also find this kind of base class in the ASP.NET framework.
- Item 17: Design and document for inheritance or else prohibit it
Someone said that, when Java lost a function there will be a corresponding annotation to fix it. This time, the lost keyword is “virtual” and its corresponding annotation after Java 1.5 is @override.
But @override cannot replace virtual, no one can stop someone override any exposed method because every non-static method is “virtual“. So the only thing we can do is writing the document!
Even when you can mark your method virtual to let other one override it in c#, there still one problem, which methods should be virtual?
“Unfortunately, there is no magic bullet. The best you can do is to think hard, take your best guess, and then test it by writing subclasses.” – Effective Java 2ed
“The only way to test a class designed for inheritance is to write subclasses.” – Effective Java 2ed
Why don’t the designer of Java make the “virtual” as a key word in Java? I think they don’t wanna you mark your method as “new” as a c# programmer can do, so do c++.
The “new” method will make some mistake, you should always escape it.
“Constructors must not invoke overridable (virtual) methods” – Effective Java 2ed
You need pay more attention when the class implemented Cloneable (ICloneable) or Serializable.
If you think no one need inherit this class, mark it as “final” (sealed).
- Item 18: Prefer interfaces to abstract classes
Both Java and c# are single inheritance (at most one direct super class), one class can implement a lot of interface. You can use this mechanism (and the Decorator pattern) to implement the multiple inheritance.
Let me say something about the diff of interface in Java and c#.
In c#, you can implement one method explicit or implicit, but in Java, all the implements are implicit.
To know how different it is, read these two articles : Dealing with Conflicting Interfaces: Part-I – Java and Dealing with Conflicting Interfaces: Part-II – .NET.
There’s no “interface” in c++. In c++ world, they use abstract classes. The interface mechanism separate the definition and implement of the abstract class.
So when you wanna define some function, prefer using interface.
Interface-based programming has a lot of benefit, it easy to TDD (Test Driven Development), DI (Dependency Injection), SOA (Service Oriented Architecture) etc.
Some one said that, there should be one interface for every three or five classes. (from Framework Design Guidelines? or IDesign c# coding standard?) The ratio of interfaces and classes in the .NET Framework obey to this rule.
You can use both interface and abstract class, but the role of interface and abstract class are different. It’s very common in practice.
There are some good examples in the Chinese book “Practical object-oriented development : c# edition” (面向对象实践之路c#版) by Li Wei (李维), the CTO of Borland, China.
This book also said that, you should use abstract class directly if no other class need to share the functions in this class. For example, the Matrix abstract class in some linear algebra library, then you can define DenceMatrix and SparseMatrix inherit from it. The MarshalByRefObject class in .NET Framework is another example.
- Item 19: Use interfaces only to define types
// Constant interface antipattern - do not use!
public interface PhysicalConstants {
// Avogadro's number (1/mol)
static final double AVOGADROS_NUMBER = 6.02214199e23;
// Boltzmann constant (J/K)
static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
// Mass of the electron (kg)
static final double ELECTRON_MASS = 9.10938188e-31;
}
This is something you can do in Java. Don’t do that, put the const field into a enum or utility class.
The only choice for c# programmer is put them in a static class, like Math class.
- Item 20: Prefer class hierarchies to tagged classes
// Tagged class - vastly inferior to a class hierarchy!
class Figure {
enum Shape {
RECTANGLE, CIRCLE
};
// Tag field - the shape of this figure
final Shape shape;
// These fields are used only if shape is RECTANGLE
double length;
double width;
// This field is used only if shape is CIRCLE
double radius;
// Constructor for circle
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}
// Constructor for rectangle
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
double area() {
switch (shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (radius * radius);
default:
throw new AssertionError();
}
}
}
// Class hierarchy replacement for a tagged class
abstract class Figure {
abstract double area();
}
class Circle extends Figure {
final double radius;
Circle(double radius) {
this.radius = radius;
}
double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle extends Figure {
final double length;
final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
double area() {
return length * width;
}
}
The code shows all the story about this theme.
OK, let say something more complex : the State Pattern (see The State Pattern: An Underappreciated Pattern)
We should always find way to replace the “if/else if/else” and “switch/case”.
I know some one use “template specializations” in c++ to do this. Woo~! Let’s go back to Java and c#.
State pattern is a good way to do this, although it is a little bit complex to implement.
The book, Agile Software Development, Principles, Patterns, and Practices, by Robert C. Martin shows you what is State Pattern.
Uncle Bob even made a tool for you to draw the state graph and automatic create the Java code for you.
- Item 21: Use function objects to represent strategies
Remember the duck story in the famous book Head First Design Patterns? Yes, it is Strategy Pattern.
You can use delegate or lambda expression to implement the Strategy Pattern in c#.
You can also use a function class to do this for you.
When you wanna put more than one methods as one strategy, the function class is the only way to do that.
// anonymous class in Java
Arrays.sort(stringArray, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
// lambda expression in c#
Arrays.sort<string>(stringArray, (s1, s2) => s1.Length - s2.Length);
- Item 22: Favor static member classes over nonstatic
Well, Let me show you the Null Object Pattern.
Most of time, when you wanna use a reference class, you should check whether it is null.
abstract class Figure
{
public abstract void Draw();
}
void DrawAll(Figure[] figures)
{
foreach (Figure figure in figures)
{
if(figure != null)
figure.Draw();
}
}
Here, I create a NullFigure inner class. When you need to return null, return Figure.NULL instead.
abstract class Figure
{
private class NullFigure : Figure
{
public override void Draw()
{
// Do nothing.
}
}
public static Figure NULL = new NullFigure();
public abstract void Draw();
}
void DrawAll(Figure[] figures)
{
foreach (Figure figure in figures)
figure.Draw();
}
The implement in Java with anonymous class looks more element.
abstract class Figure {
public static Figure NULL = new Figure() {
@Override
public void Draw() {
// Do nothing.
}
};
public abstract void Draw();
}
- iron9light‘s Item: Design is always the center of software development.
So many things changed, the way to access the database, the way to build a GUI or create web pages. The way to communicate thought internet and different operation system.
There are something look the same even twenty years before now. That is design, like the object-oriented.
Design has changed everyday. But the Design as the core of software is not changed.
There are some good books :
Design Guidelines for Developing Class Libraries by microsoft
Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin
You should read them a lot of times. It is worth to.
Only reading book cannot make you a master of design.
So the last thing is PRACTICE!