将一个类的接口转换成客户希望的另外一个接口。
适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
使用场景
主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用:
1、系统需要使用现有的类,而此类的接口不符合系统的需要。
2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
3、通过接口转换,将一个类插入另一个类系中。
简单实例
要求机器人可以模拟各种动物行为,在机器人中定义了一系列方法,如机器人叫喊方法cry()、机器人移动方法move()等。如果希望在不修改已有代码的基础上使得机器人能够像狗一样汪汪叫,像狗一样快跑,或者像鸟一样叽叽叫,像鸟一样快快飞,使用适配器模式进行系统设计。现修改实例仿生机器人,使得机器人可以像鸟一样叫,并像狗一样的跑,请绘制类图并编程实现。
UML类图
实现方式
//Bird类
public class Bird
{
public void tweedle()
{
System.out.println(“像鸟一样叫!”);
}
public void fly()
{
System.out.println(“像鸟一样飞!”);
}
}
//BirdAdapter类
public class BirdAdapter extends Bird implements Robot
{
public void cry()
{
System.out.print(“机器人模仿:”);
super.tweedle();
}
public void move()
{
System.out.print(“机器人模仿:”);
super.fly();
}
}
//Dog类
public class Dog
{
public void wang()
{
System.out.println(“像狗一样叫!”);
}
public void run()
{
System.out.println(“像狗一样跑!”);
}
}
//DogAdapter类
public class DogAdapter extends Dog implements Robot
{
public void cry()
{
System.out.print(“机器人模仿:”);
super.wang();
}
public void move()
{
System.out.print(“机器人模仿:”);
super.run();
}
}
//DBAdapter类
public class DBAdapter implements Robot{
private DogAdapter dogAdapter;
private BirdAdapter birdAdapter;
public DBAdapter() {
dogAdapter = new DogAdapter();
birdAdapter = new BirdAdapter();
}
public void cry() {
// TODO Auto-generated method stub
dogAdapter.move();
}
public void move() {
// TODO Auto-generated method stub
birdAdapter.cry();
}
}
//Robot接口
public interface Robot
{
public void cry();
public void move();
}
//XMLUtil类
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil
{
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean()
{
try
{
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File(“src/config.xml”));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName(“className”);
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
//Client类
public class Client
{
public static void main(String args[])
{
Robot robot=(Robot)XMLUtil.getBean();
robot.cry();
robot.move();
}
}
运行结果
Android开发中的实例
最常见的ListView、GridView、RecyclerView等的Adapter
实现
–
3大角色
1、目标角色,也就是所期待得到的接口。
2、需要适配的接口
3、适配器角色,是适配器模式的核心。适配器将源接口转换成目标接口
实现的要点
适配器模式分2种,类适配器模式和对象适配器模式。2种模式的区别在于实现适配的方法不同,类适配器模式通过继承需要适配的接口,而对象适配器模式则是通过组合的形式实现接口兼容的效果。因而对象适配器模式更加灵活也使用得更多,我们这里主要就介绍对象适配器模式。
我们在使用ListView时,每一项的布局和数据都不一样,但是最后输出都可以看作是一个View,这就对应了上面的适配器模式应用场景的第三条:**需要一个统一的输出接口,而输入端的接口不可预知。**下面我们来看看ListView中的适配器模式。
首先我们来看看一般我们的Adapter类的结构
class Adapter extends BaseAdapter {
private List mDatas;
public Adapter(List datas) {
mDatas = datas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public long getItemId(int position) { return position; }
@Override
public Object getItem(int position) { return mDatas.get(position);}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//初始化View
}
//初始化数据
return convertView;
}
}
可以看出Adapter里面的接口主要是getCount()返回子View的数量,以及getView()返回我们填充好数据的View,ListView则通过这些接口来执行具体的布局、缓存等工作。下面我们来简单看看ListView的实现。
首先这些getCount()等接口都在一个接口类Adapter里
public interface Adapter {
//省略其他的接口
int getCount();
Object getItem(int position);
long getItemId(int position);
View getView(int position, View convertView, ViewGroup parent);
//省略其他的接口
}
中间加了一个过渡的接口ListAdapter
public interface ListAdapter extends Adapter {
//接口省略
}
我们在编写我们自己的Adapter时都会继承一个BaseAdapter,我们来看看BaseAdapter
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
//BaseAdapter里面实现了ListAdapter的接口以及部分Adapter中的接口
//而像getCount()以及getView()这些接口则需要我们自己去实现
}
ListView的父类AbsListView中有ListAdapter接口,通过这个接口来调用getCount()等方法获取View的数量等
public abstract class AbsListView extends AdapterView implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
/**
- The adapter containing the data to be displayed by this view
*/
ListAdapter mAdapter;
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
final ViewTreeObserver treeObserver = getViewTreeObserver();
treeObserver.addOnTouchModeChangeListener(this);
if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) {
treeObserver.addOnGlobalLayoutListener(this);
}
if (mAdapter != null && mDataSetObserver == null) {
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
// Data may have changed while we were detached. Refresh.
mDataChanged = true;
mOldItemCount = mItemCount;
//通过getCount()获取View元素的个数
mItemCount = mAdapter.getCount();
}
}
}
从上面我们可以看出,AbsListView是一个抽象类,它里面封装了一些固定的逻辑,如Adapter模式的应用逻辑、布局的复用逻辑和布局子元素逻辑等。而具体的实现则是在子类ListView中。下面我们来看看ListView中是怎么处理每一个子元素View的。
@Override
protected void layoutChildren() {
总结
现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。
我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。
Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。但是周末花一天时间体验一下它的开发流程,了解一下它的优缺点、能干什么或者不能干什么。这个时间,并不是我们不能接受的。
如果有时间,其实通读一遍 Flutter 的文档,是最全面的一次对 Flutter 的了解过程。但是如果我们只有 8 小时的时间,我希望能关注一些最值得关注的点。
(跨平台开发(Flutter)、java基础与原理,自定义view、NDK、架构设计、性能优化、完整商业项目开发等)
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
总结
现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。
我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。
Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。但是周末花一天时间体验一下它的开发流程,了解一下它的优缺点、能干什么或者不能干什么。这个时间,并不是我们不能接受的。
如果有时间,其实通读一遍 Flutter 的文档,是最全面的一次对 Flutter 的了解过程。但是如果我们只有 8 小时的时间,我希望能关注一些最值得关注的点。
(跨平台开发(Flutter)、java基础与原理,自定义view、NDK、架构设计、性能优化、完整商业项目开发等)
[外链图片转存中…(img-ZP59Mk2j-1715003210525)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!