Code Refactoring 5

Replace Constructor with Factory MethodIn this part of the code refactoring tutorial, I will show you how to replace a constructor with a factory method in 2 code examples.

I also received a challenge from one of you guys that I will solve. The challenge involves how to create a singleton factory method. It sounds more complicated than it is, but by watching this you’ll learn a lot. Here are some topics you may want to brush up on: Java Reflection, Factory Design Pattern, and the Singleton Design Pattern.

If you like videos like this, it helps to tell Google with a click here

Code From the Video

Customer2.java

// Replace constructors with factories

public abstract class Customer2 {
	
	private String custRating;
	static final int PREMIER = 2;
	static final int VALUED = 1;
	static final int DEADBEAT = 0;
	
	public String getCustRating(){ return custRating; }
	
	public void setCustRating(String custRating) { this.custRating = custRating; }
	
	public static void main(String[] args){
		
		// This factory will generate specific subclasses of Customer3
		
		CustomerFactory customerFactory  = new CustomerFactory();
		
		// This assigns the methods and fields for the class Premier
		
		// This is replaced when I get rid of the Switch 
		
		// Customer3 goodCustomer = customerFactory.getCustomer(Customer3.PREMIER);
		
		Customer2 goodCustomer = customerFactory.getCustomer("Premier");
		
		System.out.println("This Customers Rating: " + goodCustomer.getCustRating());
		
		// This assigns the methods and fields for the class Deadbeat
		
		// This is replaced when I get rid of the Switch 
		
		// Customer3 badCustomer = customerFactory.getCustomer(Customer3.DEADBEAT);
		
		Customer2 badCustomer = customerFactory.getCustomer("Deadbeat");
				
		System.out.println("This Customers Rating: " + badCustomer.getCustRating());
		
	}

}

class Premier extends Customer2{
	
	Premier(){
		
		setCustRating("Premier Customer");
		
	}
	
}

class Deadbeat extends Customer2{
	
	Deadbeat(){
		
		setCustRating("Deadbeat Customer");
		
	}
	
}

class CustomerFactory{
	
	// This is a poor design because this switch needs updated
	// every time I make a new subclass
	/*
	public Customer2 getCustomer(int custType){
		
		switch (custType){
		
		case 2:
			return new Premier();
		case 0:
			return new Deadbeat();
		default:
			throw new IllegalArgumentException("Invalid Customer Type");
		
		}
		
	}
	*/
	
public Customer2 getCustomer(String custName){
		
		try {
			
			// forName returns a class object with the String that is
			// passed to it. newInstance() creates an instance of the class
			
			return (Customer2) Class.forName(custName).newInstance();
			
		}
		
		catch(Exception e){
			
			throw new IllegalArgumentException("Invalid Customer Type");
		
		}
		
	}
	
}

Athlete.java

Athlete.java

// Create a Factory that creates singletons

import java.lang.reflect.Method;

public class Athlete {
	
	private String athleteName = "";
	
	public String getAthleteName() {
		return athleteName;
	}

	public void setAthleteName(String athleteName){ this.athleteName = athleteName; }
	
	public static Athlete getInstance(){
		return null;
	}

	
}

class GoldWinner extends Athlete{
	
	// Set to null to signify that an instance of
	// type GoldWinner doesn't exist
	
	private static GoldWinner goldAthlete = null;
	
	// Constructor is set to private to keep other
	// classes from creating an instance of GoldWinner
	
	private GoldWinner(String athleteName){ 
		
		setAthleteName(athleteName);
		
	}
	
	// Creates 1 instance of GoldWinner (Simple Singleton)
	
	public static GoldWinner getInstance(String athleteName){
		
		// If an instance of GoldWinner doesn't exist
		// create one
		
		if(goldAthlete == null){
			
			goldAthlete = new GoldWinner(athleteName);
			
		} 
		
		return goldAthlete;
		
	}
	
}

class SilverWinner extends Athlete{
	
	private static SilverWinner silverAthlete = null;
	
	private SilverWinner(String athleteName){ 
		
		setAthleteName(athleteName);
		
	}
	
	public static SilverWinner getInstance(String athleteName){
		
		if(silverAthlete == null){
			
			silverAthlete = new SilverWinner(athleteName);
			
		} 
		
		return silverAthlete;
		
	}
	
}

class BronzeWinner extends Athlete{
	
	private static BronzeWinner bronzeAthlete = null;
	
	private BronzeWinner(String athleteName){ 
		
		setAthleteName(athleteName);
		
	}
	
	public static BronzeWinner getInstance(String athleteName){
		
		if(bronzeAthlete == null){
			
			bronzeAthlete = new BronzeWinner(athleteName);
			
		} 
		
		return bronzeAthlete;
		
	}
	
}

class MedalFactory{
	
	public Athlete getMedal(String medalType, String athleteName){
		
		try {
			
			// Define the type of the parameter that will be passed 
			// to the method I create below
			
			Class[] athleteNameParameter = new Class[]{String.class};
			
			// forName returns a class object with the String that is
			// passed to it. getMethod returns the method provided 
			// the second parameter defines the type of parameter passed
			// to the method
			
			Method getInstanceMethod =  Class.forName(medalType).getMethod("getInstance", athleteNameParameter);
			
			// Create an array with the parameter values that will be
			// passed to the method getInstance
			
			Object[] params = new Object[]{new String(athleteName)};
			
			// Pass the parameters to method getInstance and return
			// a subclass of type Athlete
			
			return (Athlete) getInstanceMethod.invoke(null, params);
			
		}
		
		catch(Exception e){
			
			throw new IllegalArgumentException("Invalid Medal Type");
		
		}
		
	}
	
}

class TestMedalWinner{
	
	public static void main(String[] args){
		
		MedalFactory medalFactory = new MedalFactory();
		
		Athlete goldWinner = medalFactory.getMedal("GoldWinner", "Dave Thomas");
		Athlete silverWinner = medalFactory.getMedal("SilverWinner", "Mac McDonald");
		Athlete bronzeWinner = medalFactory.getMedal("BronzeWinner", "David Edgerton");
		
		Athlete goldWinner2 = medalFactory.getMedal("GoldWinner", "Ray Kroc");
		
		System.out.println("Gold Medal Winner: " + goldWinner.getAthleteName());
		System.out.println("Silver Medal Winner: " + silverWinner.getAthleteName());
		System.out.println("Bronze Medal Winner: " + bronzeWinner.getAthleteName());
		
		// Even though I tried to create a new Object of type GoldWinner
		// it was rejected and the original object remained
		
		System.out.println("Gold Medal Winner: " + goldWinner2.getAthleteName());
		
	}
	
}

16 Responses to “Code Refactoring 5”

  1. Neil H. says:

    Hi, another great video.

    I just though I would put a quick note on this video to help others avoid the heart ache I went through.

    Despite your 500+ videos I was swearing about you (sorry) and how crappy your code was (sorry again, lol) when mine did not work.

    Note for others: these methods rely on the package qualified class name. If your code sits in a package (com.mypack ) then your classes will all need that slapped onto them. medaltype becomes “com.mypack.”+medaltype.

    Thanks for the unexpected homework – as always I learned something.

    Cheers,
    Neil

    • Derek Banas says:

      Thank you 🙂 Yes, I have to assume that people are using the same setup that I use and that sometimes causes problems. Sorry about that.

      I’m very used to getting messages about how my code sucks so that doesn’t bother me. Most of the time the error was caused because of a missing tag, quote or semicolon. Some times it is caused because my improved code sucked that day 🙂

      This tutorial has generated more grief than normal because quite literally I’m one of the few people on Earth that ever spent this much time teaching refactoring. Type “code refactoring tutorial” into Google with the quotes. Almost every returned link is either mine or from some website that copied exactly what is on my site. That put a giant bulls eye on my back for every programming expert in the world to attack.

      I’m glad you got it fixed and I know you meant no ill will with your comment.

      Thanks for the tip
      Derek

      • Neil H. says:

        I was worried, until the last line of the message, that my British sarcasm had been misinterpreted.

        Just for absolute clarity I have never had any problems with your approach or your code. And I really appreciate these tutorials.

        As you say above everyone else stops at the standard basic Java tutorials which I have done to death. I was having real problems knowing where to go next and where/what/how to improve. As a “noob” I literally didn’t know what questions to ask and I certainly “didn’t know what I didn’t know”. Then you started this series, which is great.

        As a student I should not just be copying your code, but should be extending it, and finding its limits. With this comes knowledge of where to go for help when you are not around (be it stackoverflow or the oracle Java site or etc.). The tutorials are pitched at the perfect level for that learning.

        Cheers again,
        Neil

        • Derek Banas says:

          No reason to worry 🙂 I love any positive criticism. Without it I would never improve. When I first started making videos I figured nobody would ever see them so I didn’t aim to perfect them. It wasn’t until you guys started pointing out every little error that I realized I needed to step up my game.

          I still have a long way to go. I still dream about making the perfect math tutorials, but I’m just not good enough yet. Keep me on my toes. I appreciate it!

          Derek

  2. Altin says:

    Hi Derek,
    i have a request if it is possible. Can you get off the number in front of the code please? In this time i am studying code refactoring watching your video. You are running like a Ferrari and i have just a Fiat Panda 🙂
    Thnks for your work man!!!

    • Derek Banas says:

      Hi, On the part of the page that has the code, put your mouse over it and click on the button labeled View Source it looks like <>. That will get rid of the line numbers. I hope that helps

  3. david says:

    Hi derek. Great tutorial. I have watched it quite of in a rush, there are concepts in here that I am yet to learn so I will certainly need to go back into it, maybe search for some knowledge or/and probably go back to your earlier tutorials and learn quite a lot of things that I skipped. Nevertheless I have a question: I had the impression that the if clause inside the singletons breaks the rule “the program isn’t allowed to contain conditional statements”. Am I misunderstanding what was asked by emily’s professor?
    I am a brazilian fellow studying computer science in Israel’s OpenUniversity, I love to code and I was feeling somewhat frustrated by the large amount of stuff I have to learn at school and the little amount that is code or direction to actually implement ideas. Your tutorials are amazing to do in parallel, they’ve been giving me back the “rush” of learning and desiring to learn and know more, they give me very strong feeling of purpose on what I’m learning, thank you sooo much.

    • Derek Banas says:

      Thank you very much for the compliment 🙂 At this time I can’t remember what all the rules were, but I know that the code I wrote was accepted by the teacher.

      I’m very happy that you enjoy the videos. I do my best to make original ones.

  4. david says:

    p.s: sorry, the above was meant to the singleton tutorial, hope you can delete it from here and if you desire move it to the right place. thanks again

  5. Burale says:

    Thanks again. Just one question. Is this line necessary:

    Object[] params = new Object[]{new String(athleteName)};

    getInstanceMethod.invoke(null, params);

    I was able to get away with it using the string directly like this and it works:

    getInstanceMethod.invoke(null, athleteName);

    • Derek Banas says:

      I used that line to point out that invoke receives an Object, but since String is an Object it still works. I tend to point everything out in my code even if it is sometimes not necessary.

  6. Rudy Vissers says:

    My comments On Customer2 :
    a) The 3 static PREMIER, VALUED and DEADBEAT should be commented out. No more value.
    b) I know that lots of examples are built on the fact that the java default package is used. I don’t think that it is a good idea in this case. Please always make use of a package and certainly at this level because it is not for a beginner anymore. If you use a package name then your code doesn’t work anymore because the name of the class has to be prefixed by the package and you do agree with me that in most of the cases we all use packages.
    Refactoring I did :
    String packageName = this.getClass().getPackage().getName();
    return (Customer2) Class.forName(packageName + “.” + custName).newInstance();

    • Derek Banas says:

      Yes I agree with your input. Some times I get stuck in keeping everything as basic as possible even when I should know that my audience will be far from intermediate programmers.

  7. Rudy Vissers says:

    (I didn’t thank you Mr Banas for the excellent tutorial!)
    on Athlete :
    Similar to Customer2. As the default package is used the code does not function anymore if a package (not default) is used.
    Corrections I did:
    String packageName = this.getClass().getPackage().getName();
    Method getInstanceMethod = Class.forName(packageName + “.” + medalType).getMethod(“getInstance”, athleteNameParameter);

  8. Rudy Vissers says:

    Another remark. You make use a lot of System.out.println and that is good to display some logging.
    I would recommend you at this level to make use of unit tests.
    I know that will complicate a little because of the use of JUnit but as it is a tutorial on code refactoring that will demonstrate to the users that after you make some refactoring and you run the tests that no regressions were created.
    I use most of the time maven and the archetype quickstart to create a new application and JUnit is already added to the pom (3.8). The maven plugin is now installed and configured in Eclipse by default. It means that if you ship a pom.xml and the code, your readers will be able to run your examples and normally not get any dependencies problems. But I know that the fact to build the examples on the core java packages is a good plus. You have javac then you have everything to make the examples run! My 2 cents. Thanks for your work Mr Banas.

Leave a Reply

Your email address will not be published.

Google+