是否有任何设计模式可以根据设备类型在数据之间切换



我们有一个基于Spring MVC的Web应用程序。现在我们需要修改此应用程序,使其在智能手机上正确呈现。

为此,我们将为智能手机创建单独的JSP。因此,请求来自浏览器,我们将检查请求是否来自桌面,然后我们将显示正常的 JSP,或者如果请求来自移动设备,那么我们将显示智能手机的 JSP。

我们将为此使用Spring Mobile。

在某些情况下,我们还希望限制智能手机上的数据。我们可能不会显示JSP中的所有数据。

例如,我们可能只需要在菜单中显示几个项目。桌面Web应用程序将显示完整菜单,而智能手机将显示较少的菜单项。即使桌面菜单和移动菜单有不同的 JSP,菜单项也来自数据库。

是否有任何设计模式可以帮助我们?我们不想编写这些条件来检查设备类型。

如果从 UI 角度看到菜单问题,则可以使用抽象工厂设计模式。 通常,您将有一个生成菜单的通用接口:

interface MenuFactory {
    Object createMainMenu();
    Object createSomeOtherMenu();
}

和两个实现:

public class DesktopAppMenuFactory implements MenuFactory {
    public Object createMainMenu() {
         ask dao for menus intended for desktop variant
         return ...
    }
    public Object createSomeOtherMenu() {
         ask dao for menus intended for desktop variant
         return ...
    }
}
public class MobileAppMenuFactory implements MenuFactory {
    public Object createMainMenu() {
         ask dao for menus intended for mobile variant
         return ...
    }
    public Object createSomeOtherMenu() {
         ask dao for menus intended for mobile variant
         return ...
    }
}

然后编写一个方法,该方法将创建适当的工厂给定客户端类型:

public static MenuFactory createMenuFactory(String clientType) {
    if( clientType is desktop.. ) {
        return new DesktopAppMenuFactory();
    } else if( clientType is mobile.. ) {
        return new MobileAppMenuFactory();
    }
}

并在控制器和 JSP 中使用 MenuFactory,而无需担心它是哪种变体。 这个唯一的 if 语句在 ebove 实用程序方法中 createMenuFactory() .

另一方面,如果您从数据角度看待问题,那么应用于服务层的策略模式将是合适的。 但是代码代码最终会与上述代码非常相似,*Factory重命名为*Service,实现被称为策略而不是工厂。

看看Sitemesh框架,这是一个轻量级且灵活的Java Web应用程序框架,它应用了四人帮装饰器模式,允许内容与演示的清晰分离

下面是一个示例,向您展示如何使用它。


配置

庞.xml

<dependency>
    <groupId>opensymphony</groupId>
    <artifactId>sitemesh</artifactId>
    <version>2.4.2</version>
</dependency>

WEB-INF/web.xml

<filter>
    <filter-name>sitemeshFilter</filter-name>
    <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>sitemeshFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

WEB-INF/sitemesh.xml

<?xml version="1.0" encoding="UTF-8" ?>
<sitemesh>
    <property name="decorators-file" value="/WEB-INF/sitemesh-decorators.xml" />
    <excludes file="${decorators-file}" />
    <page-parsers>
       <parser content-type="text/html" class="com.opensymphony.module.sitemesh.multipass.DivExtractingPageParser"/>
    </page-parsers>
    <decorator-mappers>
        <mapper class="com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper">
            <param name="decorator.parameter" value="decorator" />
        </mapper>
        <mapper
            class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
            <param name="decorator" value="none" />
            <param name="parameter.name" value="printable" />
            <param name="parameter.value" value="true" />
        </mapper>
        <mapper class="com.opensymphony.module.sitemesh.mapper.PageDecoratorMapper">
            <param name="property.1" value="meta.decorator" />
            <param name="property.2" value="decorator" />
        </mapper>
        <mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
            <param name="config" value="${decorators-file}" />
        </mapper>
    </decorator-mappers>
</sitemesh>

WEB-INF/sitemesh-decorators.xml

<?xml version="1.0" encoding="UTF-8" ?>
<decorators defaultdir="/WEB-INF/sitemesh">
    <decorator name="mobile" page="mobile.jsp" />
    <decorator name="tablet" page="tablet.jsp" />
    <decorator name="desktop" page="desktop.jsp" />
    <excludes>
        <pattern>*.html*</pattern>
        <pattern>*.json*</pattern>
        <pattern>*.xml*</pattern>
        <pattern>*.download*</pattern>
    </excludes>
</decorators>

模板

WEB-INF/sitemesh/mobile.jsp

<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" 
%><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><!DOCTYPE HTML>
<html>
    <head>
        <title>Mobile Template - <decorator:title /></title>
    </head>
    <body>        
        <nav class="mobile">
            <ul>
                <li>Menu 1</li>
                <li>Menu 2</li>
            </ul>
        </nav>
        <div id="wrapper">
            <decorator:body />
        </div>
    </body>
</html>

WEB-INF/sitemesh/tablet.jsp

<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" 
%><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><!DOCTYPE HTML>
<html>
    <head>
        <title>Tablet Template - <decorator:title /></title>
    </head>
    <body>        
        <nav class="tablet">
            <ul>
                <li>Menu 1</li>
                <li>Menu 2</li>
                <li>Menu 3</li>
                <li>Menu 4</li>
            </ul>
        </nav>
        <div id="wrapper">
            <decorator:body />
        </div>
    </body>
</html>

WEB-INF/sitemesh/desktop.jsp

<%@ taglib prefix="decorator" uri="http://www.opensymphony.com/sitemesh/decorator" 
%><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><!DOCTYPE HTML>
<html>
    <head>
        <title>Desktop Template - <decorator:title /></title>
    </head>
    <body>        
        <nav class="desktop">
            <ul>
                <li>Menu 1</li>
                <li>Menu 2</li>
                <li>Menu 3</li>
                <li>Menu 4</li>
                <li>Menu 5</li>
                <li>Menu 6</li>
            </ul>
        </nav>
        <div id="wrapper">
            <decorator:body />
        </div>
    </body>
</html>

映射

首页控制器.java

@RequestMapping("/")
public String home(Device device) {
    if (device.isMobile()) {
        return "mobile/home/index";
    } else if (device.isTablet()) {
        return "tablet/home/index";
    } else {
        return "desktop/home/index";       
    }
}

WEB-INF/views/mobile/home/index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><head>
    <meta name="decorator" content="mobile" />
    <title>Mobile Home Page</title>
</head>
<body>
    <p>Mobile Page Content</p>
</body>

WEB-INF/views/tablet/home/index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><head>
    <meta name="decorator" content="tablet" />
    <title>Tablet Home Page</title>
</head>
<body>
    <p>Tablet Page Content</p>
</body>

WEB-INF/views/desktop/home/index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
%><head>
    <meta name="decorator" content="desktop" />
    <title>Desktop Home Page</title>
</head>
<body>
    <p>Desktop Page Content</p>
</body>

大多数其他建议的解决方案都是过度设计的,遵循保持简单原则。

使用 JSP 生成 XML 页面的标准化抽象,该 JSP 根据客户端设备类型选择适当的不同样式表进行包含。 这样可以保持内容与演示的良好分离,而不会将自己绑定到特定设备。

如果您使用的是Spring 3 MVC,并且遵循让控制器方法返回视图的正常模式,那么我认为最简单,最正确的方法是使用单独的方法来处理移动设备或具有相同的方法,但返回不同的视图(通过视图解析器)。

因此,例如,我的方法可能会执行以下操作:

@RequestMapping
public String default(Model uiModel, HttpServletRequest request) {
  // Do common stuff here, like load your model
  if(request.getHeader("User-Agent").indexOf("Mobile") != -1) {
    return "normalForm";
  } else {
    return "mobileForm";
  }
}

您甚至可以根据一些预定义的模式动态构建名称,例如iPhone得到"form_iphone",Chrome得到"form_chrome"。

假设您打算将您的支持扩展到任何类型的设备,我会创建某种支持来帮助解决这个问题。我可以想到一个简单的方法,但我假设了很多(您的菜单是根据示例的用户权限呈现的......

根据

用户的当前设备更改用户的权限(桌面用户可以查看所有内容,移动用户的权限较少,因此无法在菜单上看到一些 itens)。您可以登录用户,并根据设备发布处理他的权限。

这可以创建,

在菜单表上添加列或创建某种处理器来删除无法为某些设备呈现的菜单元素。

请记住,智能手机有多种尺寸,这意味着两个视图是不够的。看看HTML5。您甚至可能不需要两个视图。 有一个响应式网页。响应式页面会调整自身大小以适应小型和大型外形规格。例如:http://twitter.github.com/bootstrap/index.html。尝试调整上述链接中的页面大小。您可以有一个菜单,该菜单将在较小的屏幕上自行压缩(或删除某些项目)。如果触摸在你的应用程序中很重要,请查看触摸的java脚本框架。另请参阅 http://verekia.com/initializr/responsive-template

如果您的主要目标是添加对移动网站的支持,我会考虑的另一种方法是只使用一个响应式版本的网站。

您可以使用许多css框架来实现这一点,其中最受欢迎的框架之一是 Twitter的Bootstrap 。

使用此方法,屏幕将在给定较小或较大的屏幕尺寸时相应地调整大小。您可以使用此示例查看流体布局:引导流体。

这样,您的 Web 应用程序可以调整其布局和内容(包括菜单和图像)的大小以便它可以在多种屏幕尺寸上使用。

最新更新