Conventions and patterns for multi-platform development

Chromium is a large and complex cross-platform product. We try to share as much code as possible between platforms, while implementing the UI and OS integration in the most appropriate way for each. While this gives a better user experience, it adds extra complexity to the code. This document describes the recommended practices for keeping such cross-platform code clean.
 
We use a variety of different file naming suffixes to indicate when a file should be used:
  • Mac files use the _mac suffix for lower-level files and Cocoa (Mac UI) files use the _cocoa suffix.
  • iOS files use the _ios suffix (although iOS also uses some specific _mac files).
  • Linux files use _linux suffix for lower-level files, _gtk for GTK-specific files, and _x for X Windows (with no GTK) specific files.
  • Windows files use the _win suffix.
  • Posix files shared between Mac, iOS, and Linux use the _posix suffix.
  • Files for Chrome's "Views" UI (on Windows and experimental GTK) layout system use the _views suffix.
 
The separate front-ends of the browser are contained in their own directories:
 
  • Mac Cocoa: chrome/browser/ui/cocoa
  • Linux GTK: chrome/browser/ui/gtk
  • Windows Views (and the experimental GTK-views): chrome/browser/ui/views
 
The Coding Style page lists some stylistic rules affecting platform-specific defines.

How to separate platform-specific code

Small platform differences: #ifdefs

When you have a class with many shared functions or data members, but a few differences, use #ifdefs around the platform-specific parts. If there are no significant differences, it's easier for everybody to keep everything in one place.

Small platform differences in the header, larger ones in the implementation: split the implementation

There may be cases where there are few header file differences, but significant implementation differences for parts of the implementation. For example, base/waitable_event.h defines a common API with a couple of platform differences.
 
With significant implementation differences, the implementation files can be split. The prevents you from having to do a lot of #ifdefs for the includes necessary for each platform and also makes it easier to follow (three versions each of a set of functions in a file can get confusing). There can be different .cc files for each platform, as in base/waitable_event_posix.cc that implements posix-specific functions. If there were cross-platform functions in this class, they would be put in a file called base/waitable_event.cc.

Complete platform implementations and callers: separate implementations

When virtually none of the implementation is shared, implement the class separately for each platform in separate files.
 
If all implementations are in a cross-platform directory such as base, they should be named with the platform name, such as FooBarWin in base/foo_bar_win.h. This case will generally be rare since files in these cross-platform files are normally designed to be used by cross-platform code, and separate header files makes this impossible. In some places we've defined a commonly named class in different files, so PlatformDevice is defined in skia/ext/platform_device_win.h, skia/ext/platform_device_linux.h, and skia/ext/platform_device_mac.h. This is OK if you really need to refer to this class in cross-platform code. But generally, cases like this will fall into the following rule.
 
If the implementations live in platform-specific directories such as chrome/browser/ui/cocoa or chrome/browser/ui/views, there is no chance that the class will be used by cross-platform code. In this case, the classes and filenames should omit the platform name since it would be redundant. So you would have FooBar implemented in chrome/browser/ui/cocoa/foo_bar.h.
 
Don't create different classes with different names for each platform and typedef it to a shared name. We used to have this for PlatformCanvas, where it was a typedef of PlatformCanvasMac, PlatformCanvasLinux, or PlatformCanvasWin depending on the platform. This makes it impossible to forward-declare the class, which is an important tool for reducing dependencies.

When to use virtual interfaces

In general, virtual interfaces and factories should not be used for the sole purpose of separating platform differences. Instead, it should be be used to separate interfaces from implementations to make the code better designed. This comes up mostly when implementing the view as separate from the model, as in TabContentsView or RenderWidgetHostView. In these cases, it's desirable for the model not to depend on implementation details of the view. In many cases, there will only be one implementation of the view for each platform, but gives cleaner separation and more flexibility in the future.
 
In some places like TabContentsView, the virtual interface has non-virtual functions that do things shared between platforms. Avoid this. If the code is always the same regardless of the view, it probably shouldn't be in the view in the first place.

Implementing platform-specific UI

In general, construct platform specific user interface elements from other platform-specific user interface elements. For instance, the views-specific class BrowserView is responsible for constructing many of the browser dialog boxes. The alternative is to wrap the UI element in a platform-independent interface and construct it from a model via a factory. This is significantly less desirable as it confuses ownership: in most cases of construction by factory, the UI element returned ends up being owned by the model that created it. However in many cases the UI element is most easily managed by the UI framework to which it belongs. For example, a views::View is owned by its view hierarchy and is automatically deleted when its containing window is destroyed. If you have a dialog box views::View that implements a platform independent interface that is then owned by another object, the views::View instance now needs to explicitly tell its view hierarchy not to try and manage its lifetime.
 
e.g. prefer this:
 

// browser.cc:

Browser::ExecuteCommand(..) {
  ...
  case IDC_COMMAND_EDIT_FOO:
    window()->ShowFooDialog();
    break;
  ...
}

// browser_window.h:

class BrowserWindow {
...
  virtual void ShowFooDialog() = 0;
...
};

// browser_view.cc:

BrowserView::ShowFooDialog() {
  views::Widget::CreateWindow(new FooDialogView)->Show();
}

// foo_dialog_view.cc:

// FooDialogView and FooDialogController are automatically cleaned up when the window is closed.
class FooDialogView : public views::View {
  ...
 private:
  scoped_ptr<FooDialogController> controller_; // Cross-platform state/control logic
  ...
}

 
to this:
 

// browser.cc:

Browser::ExecuteCommand(..) {
  ...
  case IDC_COMMAND_EDIT_FOO: {
    FooDialogController::instance()->ShowUI();
    break;
  }
  ...
}

// foo_dialog_controller.h:

class FooDialog {
 public:
  static FooDialog* CreateFooDialog(FooDialogController* controller);
  virtual void Show() = 0;
  virtual void Bar() = 0;
};

class FooDialogController {
 public:
  ...
  static FooDialogController* instance() {
    static FooDialogController* instance = NULL;
    if (!instance)
      instance = Singleton<FooDialogController>::get();
    return instance;
  }
  ...
 private:
  ...
  void ShowUI() {
    if (!dialog_.get())
      dialog_.reset(FooDialog::CreateFooDialog(this));
    dialog_->Show();
  }

// Why bother keeping FooDialog or even FooDialogController around?
  // Most dialogs are very seldom used.
  scoped_ptr<FooDialog> dialog_;
};

// foo_dialog_win.cc:

class FooDialogView : public views::View,
                      public FooDialogController {
 public:
  ...
  explicit FooDialogView(FooDialogController* controller) {
    set_parent_owned(false); // Now necessary due to scoped_ptr in FooDialogController.
  }
  ...
};

FooDialog* FooDialog::CreateFooDialog(FooDialogController* controller) {
  return new FooDialogView(controller);
}

Sometimes this latter pattern is necessary, but these occasions are rare, and very well understood by the frontend team. When porting, consider converting cases of the latter model to the former model if the UI element is something simple like a dialog box.

Conventions and patterns for multi-platform development的更多相关文章

  1. 环境初始化 Build and Install the Apache Thrift IDL Compiler Install the Platform Development Tools

    Apache Thrift - Centos 6.5 Install http://thrift.apache.org/docs/install/centos Building Apache Thri ...

  2. Head First Design Patterns

    From Head First Design Patterns. Design Principle: Idnetify the aspects of your application that var ...

  3. Linux之一次性安装开发工具:yum groupinstall Development tools

    [spark@sparksinglenode ~]$ yum grouplist | moreLoaded plugins: fastestmirror, refresh-packagekit, se ...

  4. Learning PHP Design Patterns

    Learning PHP Design Patterns CHAPTER 1 Algorithms handle speed of operations, and design patterns ha ...

  5. Customize Netbeans Platform Splash Screen and About Dialog

    原帖一直打不开,通过谷歌翻译找到的 http://blogs.kiyut.com/tonny/2007/10/18/customize-netbeans-platform-splash-screen- ...

  6. Best Open Source Software

    Best Open Source Software Open Source, Software, Top The promise of open source software is best qua ...

  7. BlackArch-Tools

    BlackArch-Tools 简介 安装在ArchLinux之上添加存储库从blackarch存储库安装工具替代安装方法BlackArch Linux Complete Tools List 简介 ...

  8. 所有selenium相关的库

    通过爬虫 获取 官方文档库 如果想获取 相应的库 修改对应配置即可 代码如下 from urllib.parse import urljoin import requests from lxml im ...

  9. Android NDK开发指南---Application.mk文件和android.mk文件

    https://android.googlesource.com/platform/development/+/donut-release/ndk/docs/OVERVIEW.TXT https:// ...

随机推荐

  1. 在Windows Server 2008 R2中删除网桥

    How to remove a network bridge in Windows Server 2008 R2 症状: 删除网桥的时候,按理说应该在“网络连接”中选择要被删除的网桥,右键点击,然后选 ...

  2. ACM数学知识体系

    在盛情收到学弟邀请给他们整理ACM数学方面的知识体系,作为学长非常认真的弄了好久,希望各学弟不辜负学长厚爱!!!非常抱歉因为电脑全盘格式化好多word.PPT都丢失,我尽量具体地给大家找到各知识点学习 ...

  3. mysql-安全管理

    一.管理用户 用户账号和信息存放在数据库中 use mysql; select user form user; mysql数据库中包含一个user的表,它包含所有用户账号. user表中右移个名为us ...

  4. 重写MPAndroidChart显示标记

    MPAndroidChart是实现图表功能的优秀控件, 能够完毕大多数绘制需求. 对于改动第三方库而言, 优秀的架构是继承开发, 而不是把源代码拆分出去. MP在显示标记控件(MarkView)时, ...

  5. 用html语言写一个功课表

    今天在网上看了一个关于html的教程,主要是讲表格,看完之后认为有必要上机试试.于是就写了以下的一段代码. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb ...

  6. Scala学习笔记及与Java不同之处总结-从Java开发者角度

    Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续 ...

  7. CSDN博客2014年4月24日清理缓存

    亲爱的CSDN博主们.我们将于今天(2014年4月24日)对CSDN博客频道缓存进行清理,假设您登录后发现自己的文章总数.积分.评论数.訪问数出现异常,请不要慌张.您的数据并没有丢失.将会在缓存清理完 ...

  8. 熟悉了下HTTP协议

    HTML是一种用来定义网页的文本,会HTML,就可以编写网页: HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信.200表示一个成功的响应,后面的OK是说明.失败的响应有404 Not ...

  9. Oozie框架基础

    * Oozie框架基础 官方文档地址:http://oozie.apache.org/docs/4.0.0/DG_QuickStart.html 除Oozie之外,类似的框架还有: ** Zeus:h ...

  10. C#后台请求其它网站页面

    /// <summary> /// 指定Post地址使用Get 方式获取全部字符串 /// </summary> /// <param name="url&qu ...