针对不同用户的设计模式

时间:2021-03-30 20:23:48

标签: java object oop design-patterns

我的应用中有两种不同类型的用户:LoggedInUserGuestUser 它们都具有相同的功能,例如 createNewOrdercancelOrder(实现应该不同)但 LoggedInUser 可以做更多的事情,例如 WriteReviewFavouriteProduct 等。

我想要一个针对这些类型用户的设计模式,这样我就不会检查我的应用程序目前拥有哪种类型的用户,我只是在不知道用户的实际类型的情况下调用该方法。

有没有可能做到这一点?哪种设计模式最合适?

到目前为止我所做的是创建一个名为 User 的抽象类,它具有类似的功能:

abstract class User{
    void createOrder()
    void cancelOrder()
}

还有一个 LoggedInUser 的类和一个 GuestUser 的类:

class LoggedInUser extends User {
    //some instance variables

    void favouriteProduct(String id) {
      //...
    }

    void writeReview(Review review) {
        //...
    }
  
    @override
    void cancelOrder(){
         //...
    }
  
    @override
    void cancelOrder(){
         //...
    }
}

class GuestUser extends User {
    //some instance variables
 
    @override
    void cancelOrder(){
       //...
    }

    @override
    void cancelOrder(){
       //...
    }
}

谢谢。

1 个答案:

答案 0 :(得分:1)

如果您完全反对必须检查您拥有哪种类型的用户,并且无论如何都想以相同的方式对待他们(我不确定您为什么想要那样),您可以添加默认实现抽象 favouriteProduct 类中的 writeReviewUser 方法。在 GuestUser 实例上调用它们时将使用这些默认实现,而您将在 LoggedInUser 中覆盖它们。我认为这不是一个很棒的设计。

public class UserExample
{
    public static void main(String[] args)
    {
        User loggedInUser = new LoggedInUser();
        User guestUser = new GuestUser();

        loggedInUser.favouriteProduct("123");
        guestUser.favouriteProduct("123");

        Review review = new Review();
        review.text = "This product is amazing.";

        loggedInUser.writeReview(review);
        guestUser.writeReview(review);
    }
}

abstract class User
{
    abstract void createOrder();

    abstract void cancelOrder();

    public void favouriteProduct(String id)
    {
        System.out.printf("UserAbstract::favouriteProduct(%s)%n", id);
    }

    public void writeReview(Review review)
    {
        System.out.printf("UserAbstract::writeReview%n\t%s%n", review.text);
    }
}

class LoggedInUser extends User
{
    //some instance variables

    @Override
    public void favouriteProduct(String id)
    {
        System.out.printf("LoggedInUser::favouriteProduct(%s)%n", id);
    }

    @Override
    public void writeReview(Review review)
    {
        System.out.printf("LoggedInUser::writeReview%n\t%s%n", review.text);
    }

    @Override
    public void createOrder()
    {
        //...
    }

    @Override
    public void cancelOrder()
    {
        //...
    }
}

class GuestUser extends User
{
    //some instance variables

    @Override
    public void createOrder()
    {
        //...
    }

    @Override
    public void cancelOrder()
    {
        //...
    }
}


class Review
{
    public String text;
}

最好将 favouriteProductwriteReview 方法移至服务类。这些方法可以接受实现 User 接口的实例,然后决定如何以不同的方式处理 LoggedInUsersGuestUsers 的实例。这允许您非常通用地传递用户,并且仅在绝对必要时才分支。您可以忽略服务方法中的 GuestUsers,抛出异常,重定向到登录,无论您想要什么。 IMO 那种逻辑不属于您的业务对象。

public class UserExample2
{
    public static void main(String[] args)
    {
        LoggedInUser loggedInUser = new LoggedInUser();
        GuestUser guestUser = new GuestUser();

        UserService.favouriteProduct(loggedInUser, "123");
        UserService.favouriteProduct(guestUser, "123");

        Review review = new Review();
        review.text = "This product is amazing.";

        UserService.writeReview(loggedInUser, review);
        UserService.writeReview(guestUser, review);
    }
}

interface User
{
    void createOrder();

    void cancelOrder();
}

class LoggedInUser implements User
{
    //some instance variables

    public void createOrder()
    {
        //...
    }

    public void cancelOrder()
    {
        //...
    }
}

class GuestUser implements User
{
    //some instance variables

    public void createOrder()
    {
        //...
    }

    public void cancelOrder()
    {
        //...
    }
}

class UserService
{
    public static void favouriteProduct(LoggedInUser user, String id)
    {
        System.out.printf("UserService::favouriteProduct LoggedInUser favourites %s%n", id);
    }

    public static void favouriteProduct(GuestUser user, String id)
    {
        System.out.printf("UserService::favouriteProduct GuestUser favourites %s%n", id);
    }

    public static void writeReview(LoggedInUser user, Review review)
    {
        System.out.printf("UserService::writeReview LoggedInUser writes%n\t%s%n", review.text);
    }

    public static void writeReview(GuestUser user, Review review)
    {
        System.out.printf("UserService::writeReview GuestUser writes%n\t%s%n", review.text);
    }
}

class Review
{
    public String text;
}

[编辑]

这里有一个更详尽的示例,说明如何解决此类问题。这更清楚地说明了如何使用多态和继承在典型的电子商务购物车场景中跨不同类型的会话实现通用功能。

import java.util.*;

public class UserExample3
{
    public static void main(String[] args)
    {
        // Set up our user with test data IRL this would be retrieved from authentication mechanism
        Address userAddress = new Address("1313 Mockingbird Lane", "London", "GB", "12345");
        User loggedInUser = new User(23, "Alice", userAddress);

        // Set up a session for our user
        UserSession userSession = new UserSession(loggedInUser);

        // Set up a guest session
        GuestSession guestSession = new GuestSession();

        // Add items - action happens in the abstract class
        userSession.addOrderItem("123", 2);
        guestSession.addOrderItem("456", 3);

        // Favourite items - action happens in the concrete classes
        userSession.favouriteProduct("123");
        guestSession.favouriteProduct("456");

        // List items via the cart service, requires instances of Session interface
        CartService.listCartItems(userSession);
        CartService.listCartItems(guestSession);

        // Write reviews via the Review service, requires instances of Session interface
        Review userReview = ReviewService.writeReview(userSession, "123", "This product is amazing");
        System.out.println(userReview);

        Review guestReview = ReviewService.writeReview(guestSession, "456", "This product is pants");
        System.out.println(guestReview);

        // Create orders via the order service, requires instances of Session interface
        Order userOrder = null;
        try
        {
            userOrder = OrderService.createOrderFromSession(userSession);
            System.out.println(userOrder);
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }

        // Guest checkout requires us to collect an address (user already has address saved in their profile)
        Address guestAddress = new Address("1060 W. Addison", "Chicago", "US", "12345");
        guestSession.setAddress(guestAddress);

        Order guestOrder = null;
        try
        {
            guestOrder = OrderService.createOrderFromSession(guestSession);
            System.out.println(guestOrder);
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }

        // Estimate shipping charges via cart service
        double userEstimate = CartService.estimateShipping(userSession);
        System.out.printf("User shipping estimate: %.2f%n", userEstimate);

        double guestEstimate = CartService.estimateShipping(guestSession);
        System.out.printf("Guest shipping estimate: %.2f%n", guestEstimate);

        // Convert a guest session to a user session via CartService - requires concrete classes
        UserSession newUserSession = new UserSession(loggedInUser);
        CartService.populateUserSessionCartFromGuestSession(newUserSession, guestSession);

            /*
                The guest session items are now in the user session
                and the guest session favorites have been added to the user

                Requires instances of Session interface
             */
        CartService.listCartItems(newUserSession);
        System.out.println(loggedInUser);

        // Empty the cart via the cart service
        CartService.emptyCart(newUserSession);
        CartService.listCartItems(newUserSession);

        // Cancel an order via the order service - will be null if validation failed
        if(userOrder != null)
        {
            OrderService.cancelOrder(userOrder);
            System.out.println(userOrder);
        }

        // Complete an order via the order service - will be null if validation failed
        if(guestOrder != null)
        {
            OrderService.completeOrder(guestOrder);
            System.out.println(guestOrder);
        }
    }
}

/**
 * Do cart things
 */
class CartService
{
    /**
     * Print the line items in a cart
     * <p>
     * This is an example of polymorphism - any instance that implements the
     * Session interface will work here
     *
     * @param session instance of interface Session
     */
    public static void listCartItems(Session session)
    {
        System.out.println("Order items:");
        for (Map.Entry<String, Integer> currItem : session.getOrderItems().entrySet())
        {
            System.out.printf("\t%s: %d%n", currItem.getKey(), currItem.getValue());
        }
    }

    /**
     * Populate a UserSession with data collected in a guest session
     * Used when a user begins their order while unauthenticated, the logs in prior to completing the order
     *
     * @param userSession  Instance of UserSession
     * @param guestSession Instance of GuestSession
     */
    public static void populateUserSessionCartFromGuestSession(UserSession userSession, GuestSession guestSession)
    {
        userSession.getOrderItems().putAll(guestSession.getOrderItems());

        userSession.getUser().getFavouriteProducts().addAll(guestSession.getFavouriteProducts());

        guestSession.clearOrderItems();
    }

    /**
     * Clear the items from a cart
     * <p>
     * This is an example of polymorphism - any instance that implements the
     * Session interface will work here
     *
     * @param session Instance of Session interface
     */
    public static void emptyCart(Session session)
    {
        session.clearOrderItems();
    }

    public static double estimateShipping(Session session)
    {
        Map<String, Double> perItemChargesByCountry = new HashMap<>();
        perItemChargesByCountry.put("US", 1.23);
        perItemChargesByCountry.put("GB", 1.05);

        String countryCode = session.getAddress().getCountry();
        Double perItemCharge = perItemChargesByCountry.get(countryCode);

        int numItems = 0;
        for (Map.Entry<String, Integer> currLineItem : session.getOrderItems().entrySet())
        {
            numItems += currLineItem.getValue();
        }

        return numItems * perItemCharge;
    }
}

/**
 * Do order things
 */
class OrderService
{
    /**
     * Create an order from the line items in a cart
     * <p>
     * This is an example of polymorphism - any instance that implements the
     * Session interface will work here
     * <p>
     * If the session is an instance of UserSession, data available only on
     * those instances will be set on the order record
     *
     * @param session Instance of Session interface
     * @return Order
     */
    public static Order createOrderFromSession(Session session) throws Exception
    {
        Order order = new Order();
        order.setStatus(Order.Status.PENDING);
        order.setAddress(session.getAddress());
        order.getItems().putAll(session.getOrderItems());

        if (session instanceof UserSession)
        {
            order.setUser(((UserSession) session).getUser());
        }

        if(!order.validate())
        {
            throw new Exception("Something is wrong with the order");
        }

        return order;
    }

    /**
     * Cancel an order by changing its status
     *
     * @param order Order
     */
    public static void cancelOrder(Order order)
    {
        order.setStatus(Order.Status.CANCELLED);
    }

    /**
     * Complete an order by changing its status
     *
     * @param order Order
     */
    public static void completeOrder(Order order)
    {
        order.setStatus(Order.Status.COMPLETE);
    }
}

/**
 * Do review things
 */
class ReviewService
{
    /**
     * Create a product review
     * <p>
     * This is an example of polymorphism - any instance that implements the
     * Session interface will work here
     * <p>
     * If the session is an instance of UserSession, data available only on
     * those instances will be set on the review record
     *
     * @param session   Session
     * @param productId String
     * @param text String content of review
     * @return Review
     */
    public static Review writeReview(Session session, String productId, String text)
    {
        Review review = new Review();

        if (session instanceof UserSession)
        {
            review.setUser(((UserSession) session).getUser());
        }

        review.setProductId(productId);
        review.setText(text);

        return review;
    }
}

/**
 * Describe the contract that all session classes should adhere to
 */
interface Session
{
    Address getAddress();

    Map<String, Integer> getOrderItems();

    void setOrderItems(Map<String, Integer> orderItems);

    void addOrderItem(String productId, int quantity);

    void removeOrderItem(String productId);

    void clearOrderItems();

    void favouriteProduct(String productId);
}

/**
 * Implement functionality common to all concrete session classes
 */
abstract class SessionAbstract
{
    private Map<String, Integer> orderItems = new HashMap<>();

    public Map<String, Integer> getOrderItems()
    {
        return orderItems;
    }

    public void setOrderItems(Map<String, Integer> orderItems)
    {
        this.orderItems = orderItems;
    }

    public void addOrderItem(String productId, int quantity)
    {
        if (orderItems.containsKey(productId))
        {
            quantity += orderItems.get(productId);
        }

        orderItems.put(productId, quantity);
    }

    public void removeOrderItem(String productId)
    {
        orderItems.remove(productId);
    }

    public void clearOrderItems()
    {
        orderItems.clear();
    }
}

/**
 * Concrete session class for authenticated users
 */
class UserSession extends SessionAbstract implements Session
{
    private final User user;

    public UserSession(User user)
    {
        this.user = user;
    }

    public User getUser()
    {
        return user;
    }

    @Override
    public Address getAddress()
    {
        return user.getAddress();
    }

    public void favouriteProduct(String productId)
    {
        user.addFavouriteProduct(productId);
    }
}

/**
 * Concrete session class for guest users
 *
 * We need to have an instance of Address and collection of favourite items
 * here since we don't have a user instance
 */
class GuestSession extends SessionAbstract implements Session
{
    private Address address;
    Set<String> favouriteProducts = new TreeSet<>();

    public Address getAddress()
    {
        return address;
    }

    public void setAddress(Address address)
    {
        this.address = address;
    }

    public Set<String> getFavouriteProducts()
    {
        return favouriteProducts;
    }

    public void favouriteProduct(String productId)
    {
        favouriteProducts.add(productId);
    }
}

class Address
{
    String street;
    String city;
    String country;
    String postCode;

    public Address(String street, String city, String country, String postCode)
    {
        this.street = street;
        this.city = city;
        this.country = country;
        this.postCode = postCode;
    }

    public String getStreet()
    {
        return street;
    }

    public void setStreet(String street)
    {
        this.street = street;
    }

    public String getCity()
    {
        return city;
    }

    public void setCity(String city)
    {
        this.city = city;
    }

    public String getCountry()
    {
        return country;
    }

    public void setCountry(String country)
    {
        this.country = country;
    }

    public String getPostCode()
    {
        return postCode;
    }

    public void setPostCode(String postCode)
    {
        this.postCode = postCode;
    }

    @Override
    public String toString()
    {
        return "Address{" + "street='" + street + '\'' + ", city='" + city + '\'' + ", country='" + country + '\'' + ", postCode='" + postCode + '\'' + '}';
    }
}

class User
{
    Integer id;
    String name;
    Address address;
    Set<String> favouriteProducts = new TreeSet<>();

    public User(Integer id, String name, Address address)
    {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    public Integer getId()
    {
        return id;
    }

    public void setId(Integer id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public Address getAddress()
    {
        return address;
    }

    public void setAddress(Address address)
    {
        this.address = address;
    }

    public Set<String> getFavouriteProducts()
    {
        return favouriteProducts;
    }

    public void setFavouriteProducts(Set<String> favouriteProducts)
    {
        this.favouriteProducts = favouriteProducts;
    }

    public void addFavouriteProduct(String productId)
    {
        favouriteProducts.add(productId);
    }

    @Override
    public String toString()
    {
        return "User{" + "id=" + id + ", name='" + name + '\'' + ", address=" + address + ", favouriteProducts=" + favouriteProducts + '}';
    }
}

class Order
{
    public enum Status
    {
        PENDING, CANCELLED, COMPLETE
    }

    private Status status;
    private User user;
    private Address address;
    private Map<String, Integer> items = new HashMap<>();

    public User getUser()
    {
        return user;
    }

    public void setUser(User user)
    {
        this.user = user;
    }

    public Address getAddress()
    {
        return address;
    }

    public void setAddress(Address address)
    {
        this.address = address;
    }

    public Map<String, Integer> getItems()
    {
        return items;
    }

    public void setItems(Map<String, Integer> items)
    {
        this.items = items;
    }

    public Status getStatus()
    {
        return status;
    }

    public void setStatus(Status status)
    {
        this.status = status;
    }

    public boolean validate()
    {
        boolean valid = true;

        if(items.isEmpty())
        {
            valid = false;
        }
        else if(address == null)
        {
            valid = false;
        }
        else if(status == null)
        {
            valid = false;
        }

        return valid;
    }

    @Override
    public String toString()
    {
        return "Order{" + "status=" + status + ", user=" + user + ", address=" + address + ", items=" + items + '}';
    }
}

class Review
{
    private String productId;
    private String text;
    private User user;

    public String getProductId()
    {
        return productId;
    }

    public void setProductId(String productId)
    {
        this.productId = productId;
    }

    public String getText()
    {
        return text;
    }

    public void setText(String text)
    {
        this.text = text;
    }

    public User getUser()
    {
        return user;
    }

    public void setUser(User user)
    {
        this.user = user;
    }

    @Override
    public String toString()
    {
        String name = (user != null) ? user.getName() : "Anonymous Coward";

        return "Review{" + "productId='" + productId + '\'' + ", text='" + text + '\'' + ", user=" + name + '}';
    }
}