Mastering Strategy and Factory Patterns in Spring Boot for Dynamic Advertising Pages
This article demonstrates how to replace complex if‑elseif logic with the Strategy pattern combined with simple and reflection‑based factories in a Spring Boot application, enabling clean, extensible handling of multiple advertising page types.
Application scenario: a third‑party interface returns four status strings (pdpAdvertisePage, plpAdvertisePage, shoppingCartAdvertisePage, checkoutAdvertisePage) that determine which advertising page to display.
First attempted solution used many if‑elseif statements, which became unreadable and was abandoned.
Second solution applies the Strategy pattern together with a simple factory, mapping each status to a concrete strategy class and making the logic clear.
Third solution replaces the simple factory with a reflection‑based factory, achieving the Open/Closed principle and allowing new page types to be added without modifying existing code.
public interface GetPointAdvertisePageStrategey {
String getAdvertisePage(Model model);
}Concrete strategy implementations:
@Component
public class PdpAdvertisePageStrategey implements GetPointAdvertisePageStrategey {
@Override
public String getAdvertisePage() {
return "pdp-advertise.jsp";
}
} @Component
public class PlpAdvertisePageStrategey implements GetPointAdvertisePageStrategey {
@Override
public String getAdvertisePage() {
return "plp-advertise.jsp";
}
} @Component
public class ShopingCartAdvertisePageStrategey implements GetPointAdvertisePageStrategey {
@Override
public String getAdvertisePage() {
return "shoppingcart-advertise.jsp";
}
} @Component
public class CheckoutAdvertisePageStrategey implements GetPointAdvertisePageStrategey {
@Override
public String getAdvertisePage() {
return "checkout-advertise.jsp";
}
}Simple factory that registers all strategies:
public class GetPointAdvertisePageSimpleFactory {
public static final String PDP_ITEM_ADVERTISE = "PDP_ITEM_ADVERTISE";
public static final String PLP_ITEM_ADVERTISE = "PLP_ITEM_ADVERTISE";
public static final String CHECKOUT_ITEM_ADVERTISE = "CHECKOUT_ITEM_ADVERTISE";
public static final String SHOPPINGCART_ITEM_ADVERTISE = "SHOPPINGCART_ITEM_ADVERTISE";
private static Map<String, GetPointAdvertisePageStrategey> recommendStrategyMap = new HashMap<>();
@Autowired private CheckoutAdvertisePageStrategey checkoutAdvertisePageStrategey;
@Autowired private PdpAdvertisePageStrategey pdpAdvertisePageStrategey;
@Autowired private PlpAdvertisePageStrategey plpAdvertisePageStrategey;
@Autowired private ShopingCartAdvertisePageStrategey shopingCartAdvertisePageStrategey;
protected void init() {
recommendStrategyMap.put(PDP_ITEM_ADVERTISE, pdpAdvertisePageStrategey);
recommendStrategyMap.put(PLP_ITEM_ADVERTISE, plpAdvertisePageStrategey);
recommendStrategyMap.put(CHECKOUT_ITEM_ADVERTISE, checkoutAdvertisePageStrategey);
recommendStrategyMap.put(SHOPPINGCART_ITEM_ADVERTISE, shopingCartAdvertisePageStrategey);
}
public GetPointAdvertisePageStrategey getStrategey(String pageType) {
if (Validator.isNullOrEmpty(recommendStrategyMap)) { init(); }
return recommendStrategyMap.get(pageType);
}
}Reflection‑based factory that creates strategy instances from a configuration file:
@Component
public class GetPointAdvertisePageReflectFactory {
@Autowired private ApplicationContext context;
private static final Logger LOGGER = LoggerFactory.getLogger(GetPointAdvertisePageReflectFactory.class);
public GetPointAdvertisePageStrategey getAdvertisePageStrategey(String filePathName, String key) {
try {
String classPath = PropertyUtil.get(filePathName, key);
if (Validator.isNullOrEmpty(classPath)) return null;
Class<?> handler = Class.forName(classPath);
return (GetPointAdvertisePageStrategey) context.getAutowireCapableBeanFactory()
.createBean(handler, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
} catch (Exception e) {
LOGGER.error("Failed to reflect the corresponding object through the specified path, filePath:{},key:{}", filePathName, key);
return null;
}
}
}Utility class for reading properties files with caching:
public class PropertyUtil {
private static Map<String, Properties> cache = new HashMap<>();
public static String get(String configFileName, String key) {
return getProperties(configFileName).getProperty(key);
}
public static Properties getProperties(String configFileName) {
if (Validator.isNotNullOrEmpty(cache.get(configFileName))) {
return cache.get(configFileName);
}
InputStream inputStream = PropertyUtil.class.getResourceAsStream(configFileName);
Properties properties = new Properties();
try { properties.load(inputStream); } catch (IOException e) { e.printStackTrace(); }
cache.put(configFileName, properties);
return properties;
}
}Test class showing how the reflection factory loads a strategy at runtime:
public class ItemRecommendReflectFactoryTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ItemRecommendReflectFactoryTest.class);
@Autowired private GetPointAdvertisePageReflectFactory getItemRecommendStrategey;
@Test
public void refectFactoryTest() {
String path = "/itemrecommend.properties";
GetPointAdvertisePageStrategey strategy = getItemRecommendStrategey.getAdvertisePageStrategey(path, "pdp.item.advertise");
if (Validator.isNotNullOrEmpty(strategy)) {
LOGGER.info("Successfully obtained the strategy via configuration and reflection");
LOGGER.info(strategy.getAdvertisePage());
}
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
