如何基于基类枚举实现页面类方法复用 消除代码冗余
goToParticularTableSection to a Base Class (Eliminating Duplication) Great question! This is a perfect opportunity to leverage abstraction, generics, and interfaces to pull that repetitive switch logic into a reusable base class. Here's how to do it cleanly:
Step 1: Create a Common Interface for Section Enums
First, define a shared interface that all your page-specific section enums will implement. This lets the base class treat any section enum uniformly:
public interface TableSection { // Optional: Add a method to get a human-readable name for logging default String getSectionName() { return this.toString(); } }
Step 2: Update Your Page-Specific Enums
Modify each of your existing section enums to implement this interface. For example:
// For your Draggable page public enum TableSectionDraggablePage implements TableSection { DefaultFunctionality, ConstrainMovement, // Add other sections here } // For another page (e.g., Sortable) public enum TableSectionSortablePage implements TableSection { SortableLists, GridSorting }
Step 3: Build the Reusable Base Page Class
Create an abstract base class that uses generics to work with any enum implementing TableSection. The base class will handle the common logic (clicking, logging), while forcing subclasses to provide the mapping between enum values and their corresponding UI elements:
public abstract class BasePage<T extends Enum<T> & TableSection> { // Assume your base class already has common utilities like clickOnElement, TestLogger, etc. protected abstract Map<T, WebElement> getSectionElementMap(); public void goToParticularTableSection(T targetSection) { // Fetch the element from the subclass-provided map WebElement sectionHeader = getSectionElementMap().get(targetSection); // Guard against invalid sections to avoid NPEs if (sectionHeader == null) { throw new IllegalArgumentException("Unknown section: " + targetSection.getSectionName()); } // Reuse your existing click logic clickOnElement(sectionHeader); TestLogger.log.info(targetSection.getSectionName() + " clicked"); } // If you don't already have this in a base class, add it here protected void clickOnElement(WebElement element) { element.click(); // Add waits or other common click logic here if needed } }
Step 4: Implement the Mapping in Your Concrete Page Classes
Each page class will extend the base class, specify its corresponding enum type, and provide the mapping between enum values and page elements:
public class DraggablePage extends BasePage<TableSectionDraggablePage> { // Your page elements (initialized via PageFactory or similar) @FindBy(id = "default-functionality-header") private WebElement defaultFunctionalityTableHeader; @FindBy(id = "constrain-movement-header") private WebElement constraintMovementTableHeader; @Override protected Map<TableSectionDraggablePage, WebElement> getSectionElementMap() { Map<TableSectionDraggablePage, WebElement> sectionMap = new HashMap<>(); sectionMap.put(TableSectionDraggablePage.DefaultFunctionality, defaultFunctionalityTableHeader); sectionMap.put(TableSectionDraggablePage.ConstrainMovement, constraintMovementTableHeader); // Add mappings for other sections here return sectionMap; } }
Step 5: Use the Refactored Code
Now you can call the method exactly like before, but the logic is centralized in the base class:
DraggablePage draggablePage = new DraggablePage(); draggablePage.goToParticularTableSection(TableSectionDraggablePage.DefaultFunctionality);
Alternative Optimization: Bind Locators Directly to Enums
If you prefer not to maintain a Map in each page, you can modify the TableSection interface to hold element locators (like By objects) directly. This keeps the enum and its UI binding tightly coupled:
- Update the interface:
public interface TableSection { By getLocator(); default String getSectionName() { return this.toString(); } }
- Update the enum to include locators:
public enum TableSectionDraggablePage implements TableSection { DefaultFunctionality(By.id("default-functionality-header")), ConstrainMovement(By.id("constrain-movement-header")); private final By locator; TableSectionDraggablePage(By locator) { this.locator = locator; } @Override public By getLocator() { return locator; } }
- Simplify the base class:
public abstract class BasePage<T extends Enum<T> & TableSection> { protected WebDriver driver; public BasePage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); } public void goToParticularTableSection(T targetSection) { WebElement sectionHeader = driver.findElement(targetSection.getLocator()); clickOnElement(sectionHeader); TestLogger.log.info(targetSection.getSectionName() + " clicked"); } protected void clickOnElement(WebElement element) { element.click(); } }
This approach eliminates the need for per-page Maps entirely, which can be cleaner if your elements are located dynamically rather than via PageFactory.
内容的提问来源于stack exchange,提问作者Dawid

