范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

老板问我怎么用面向对象的思维编程?

  面向过程OR面向对象面向过程的代码
  在说面向对象前我们来说说什么是面向过程。什么是面向过程呢?
  面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
  举个栗子,比如之前项目组做的付款计划,这里面包含了 A付款,B付款,C付款,D付款 等模块。
  拿 A付款 模块来说,我们在提交付款时,要求: 1.提交前的业务校验(如判断付款金额,预留额度) 2.单据信息填充(金额信息,银行信息,用户信息) 3.付款信息推送第三方系统(如结算系统) 4.信息推送后更新单据信息(单据状态,更新占用额度) 5.消息通知责任人处理付款信息(邮件通知,OA通知,短信通知,微信通知)
  看到这个需求我们会觉得很简单嘛,功能已经很明确,按着这个说明一行一行写代码就行了,于是我们开写: public void submitPayInfo(PayInfoyCmd createParam) {      validateBeforeSubmit(createParam);     updatePayInfo(createParam);     push2PaySettle(createParam);     updateStatusAndPayApply(createParam);     notifyExecutor(createParam); }
  我们将每个功能抽象成一个方法,相应功能写在相应的方法中。这样看上没问题,我们满足了设计原则中的单一职责原则,方法尽可能地做到了短小精悍。但我们仔细读面向过程的解释:
  面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;
  发现这不就是我们平时代码写的么,按照常规思路,我们写成了面向过程的代码。
  在我们在刚接触面向对象时就听说过面向对象了。那时书本或者网上是这么解释的
  "面向对象"是专指在程序设计中采用封装、继承、多态和抽象等设计方法。
  那么上面的案例代码也有抽象,也有封装,为什么还是算面向过程思维呢。
  那么什么是面向对象呢?
  我们从哲学上来说: 面向对象 的基本哲学是认为世界是由各种各样具有自己的运动规律和内部状态的对象所组成的;不同对象之间的相互作用和通讯构成了完整的现实世界。因此,人们应当按照现实世界这个本来面貌来理解世界,直接通过对象及其相互关系来反映世界。这样建立起来的系统才能符合现实世界的本来面目。
  这里我理解的是: 万物皆可为对象
  对象包含了自身属性与行为
  功能的交付其实是对象与对象之间的交互
  那么按照这样的想法,上述代码中的校验,三方系统推送,消息通知应该属于各自对象的行为。我们需要创建各自的对象去装载各自的行为。 public void submitPayInfo(PayInfoCmd createParam) {      payInfoSubmitValidator.validateBeforeSubmit(createParam);     updatePayInfo(createParam);     settleApplicationService.push2PaySettle(createParam);     updateStatusAndPayApply(createParam);     notifyHandler.notifyExecutor(createParam); }低耦合,关注功能本身
  要想写出面向对象思维的代码,我们需要遵循面向对象的原则: 高内聚,低耦合 。在面向对象思想中,
  功能交付是对象与对象之间的交付,每个对象承担自己的工作,对象与对象之间应该尽量减少耦合。因此我们需要降低对象之间的耦合,关注对象功能本身。
  我们将上述案例代码继续抽象。发现主要做了几件事情: 前置处理-校验
  本身功能处理
  后置处理
  其实大部分函数方法都可以抽象成三步。
  如果我们不考虑 第3点 的后置处理。 1,2点 就是我们常见的模型。对于一般方法,我们可以抽象为: 1.非业务 2.业务
  我们在复用代码时发现有的情况下是不需要有校验存在的。
  例如在提交时,我们需要业务校验,但是在保存时,又不需要。为此常见做法是在入参添加一个字段: private boolean needSubmit;
  在写代码时,使用if判断,如果needSubmit传true,调用校验方法,传false反之。所以每次写类似代码时,我们都要为是不是一定需要校验操心。
  程序员无法专注与本身业务处理,对于软件质量来说。未必是件好事。
  那么这里我们需要一个低耦合,可插拔的设计。
  这里我决定使用注解。
  tips:善用注解,但别滥用
  注解虽然降低了代码耦合度,简化了开发过程。但内部使用了反射,在一定程度上牺牲了性能。
  注解大家应该不陌生,我们使用Spring系列框架开发,就一定会用到注解,但是我相信大家很少自己开发注解。
  说回正题,我们如何使用注解开发校验功能呢?
  我们来看直接使用示例:
  上述 A付款 提交功能中,提交功能代码里面就是单纯的提交功能,对于校验功能,我们写在了注解中: @ValidatorHandler(validators = PayInfoSubmitValidator.class)
  上面的案例大家可能只会觉得:这个跟代码写在校验类里面直接调用有什么区别呢?
  那我们再来看一个例子,大家都有用过导出功能,比如EasyExcel导出。那么使用这个EasyExcel导出时,我们可以抽象为: 1.查询数据结果集 2.调用EasyExcel方法进行导出
  事实上第2点我们可以交给注解去做,程序员只需要将结果集返回即可。
  那么使用注解后:
  使用注解除了简化业务代码,还有一个重要功能:
  例如在上面的注解校验案列中,我们使用注解校验。后续维护的时候,程序员就不需要进入主体代码,只需要在对应的校验类里面维护即可,保证功能的安全性。 利用面向对象思维简化代码
  我们在编写代码时,需要思考, 这段代码是否可以重复利用
  这段代码是否可以不写
  关于重复利用,我们经常会做,比如抽取成公共的方法。关于代码是否可以不写,我们可能会思考得比较少,一般判断代码是否可以省略,需要看这段代码是不是通用功能。比如我们可以使用拦截器,注解,Spring框架的AOP来减少不必要的代码。
  同样举个例子:
  之前业务开发时,有一个字段接收的数据是Json格式的,并且需要以Json形式入库:
  如上图,数据库有个字段survey_conclusion_options数据是以Json形式储,这里我们要求: 1.create创建时,前端准入Json形式的字符串存储; 2.查询展示时,以List对象形式展示
  那么常规情况下我们会在入库时直接传Json格式的数据,示例如下:
  实体类: public class ConclusionTemplate extends BaseEntity implements Serializable {         ...     @ApiModelProperty(value = "结论选项(json形式存储)")     @Column(name = "conclusion_options")     private String conclusionOptionsJsonArrays;     ... }
  入库: public int createConclusionTemplate(ConclusionTemplate conclusionTemplate) {     BusinessExceptionAssert.checkNotNull(conclusionTemplate, "参数不能为空!!!");     conclusionTemplate.setConclusionOptionsJsonArrays("前端传入")     return this.save(conclusionTemplate); }
  展示: @Data public class ConclusionTemplateVo {     private List conclusionOptions; }public ConclusionTemplateVo getVoById(String id) {     ConclusionTemplateVo vo = new ConclusionTemplateVo();     ConclusionTemplate template = getById(id);    vo.setConclusionOptions(JsonUtils.str2list(template.getConclusionOptionsJsonArrays,ConclusionTemplateOptionVo.class)); }
  这转换我们抽象为: JsonArray ->  JsonUtils转换 -> List实体
  插入的时候我们抽象为: JsonArray -> 数据库 或  List实体  -> JsonUtils转换 -> JsonArray
  那么我们每次存取时都需要在List与Json之间转换。并且程序员需要写这段代码。
  但是在面向对象的思想中,这个Json数组中的每个元素就是一个对象,我们可不可以在代码层中以List的形式存,然后以List的形式取出,中间的Json转换有程序自动去做,不需要开发去手动转。
  我们可以在Entity类中这样写:
  我们在实体类中的这个字段写成List形式的,在上面添加@ColumnType注解。这个注解用来实现List与Json之间的自动互转。然后需要在Mapper.xml文件中配置: 
  我们在这个字段添加typeHandler。
  这样我们下代码存数据的时候就是添加List而不是Json了。
  我们来看ConclusionTemplateTypeHandler中的写法
  通过ConclusionTemplateTypeHandler,我们实现了Json与List的自动互转。 使用设计模式实现面向对象设计
  设计模式相信大家很熟悉,在面试的时候也会被问道:设计模式了解么?
  常见的设计模式如: 单列,工厂,代理,策略 等等。今天我来分享我常用的几种这几模式: 策略模式
  策略模式是一种比较简单的设计模式,生活中做成一件事有几种不同的策略选择供你达成。比如你上班可以选择坐公交上班,可以选择坐地铁上班,也可以选择自驾上班,甚至还可以步行上班。
  如果抽象程代码: if("1".equals(status)) {    return "公交"; } else if("2".equals(status)) {    return "地铁"; } else if("3".equals(status)) {    return "自驾"; } else if("4".equals(status)) {    return "步行"; }
  上面的代码有什么问题呢,就是耦合第很高,如果需要增加、移除、修改算法,需要在这里反复修改,违反了开闭原则。这个问题可以使用策略模式解决。
  如何换成策略模式是什么样的呢?
  上图我们可以知道:策略模式就是将算法调用方与算法实现方分割开来,实现两者之间的解耦。
  我认为策略模式优点是: 算法可自由切换
  避免多重if-else语句
  更好的扩展性
  我们来举个列子:
  文章开头的案列中,在提交付款最后,我们需要发消息通知,例如发OA通知: notifyHandler.notifyExecutor(createParam);
  这里 付款 提交需要发OA通知,同样的 采购付款,预付款,紧急预付款 也需要发OA通知。这里我们不就可以使用策略模式来做么:
  在主体方法我们只需要调用: notifyStrategy.notifyExecutor(createParam);
  各自模块的功能: NotifyStrategy    |__ PayXXANotifyStrategy    |__ PayXXBNotifyStrategy    |__ PayXXCNotifyStrategy    |__ PayXXDNotifyStrategypublic interface NotifyStrategy {    void notifyExecutor(PayInfoCmd createParam); }
  各自策略类实现NotifyStrategy。那么以后我们修改消息通知功能就只需要在策略类中修改,不需要去主体功能方法中。 观察者模式
  什么是观察者模式呢?
  在现实生活中,许多对象都不是独立存在的,其中一个对象的改变往往会导致其它对象的改变。比如:到了下班时间你会下班回家,路上遇到红灯你会停下来,股市行情好了你会追加投资。
  因此我们可以抽象为: 功能A运行,触发了功能B的运行。
  你可能不知道观察者模式这个名词,但你一定用过:例如消息队列的 发布-订阅模型 (生产-消费),我们拿kafka举例:
  kafka发布消息:  ListenableFuture future = kafkaTemplate.send(topic, jsonString);
  消费者订阅消息: @KafkaListener(topics = "${spring.kafka.topic}")  public void listen(ConsumerRecord<?, ?> record) {         log.info("topic={}, offset={}, message={}", record.topic(), record.offset(), record.value());  }
  除了消息中间件的发布-订阅模型属于观察者模式,Zookeeper的watch机制也使用了观察者模式。 public void watcherNode(String path) {     ZkClient connection = zookeeperConfig.getConnection();     connection.subscribeDataChanges(path, new IZkDataListener() {         @Override         public void handleDataChange(String dataPath, Object data) throws Exception {             log.info("路径:{}收到了节点变化:{}", dataPath, data);         }          @Override         public void handleDataDeleted(String s) throws Exception {             log.info("路径:{}收到节点删除");         }     }); }
  利用Zookeeper的watch机制,我们可以设计出很多功能,例如设计可靠性更优良(相对于Redis)的分布式锁,服务发现(注册中心)等。
  我们又回到开头的案列,在提交付款后需要发消息通知: 5.消息通知责任人处理付款信息(邮件通知,OA通知,短信通知,微信通知)
  这里的代码我们可能会这样写: public void notifyExecutor(PayInfoCmd createParam) {     emailNotifyApplicationService.sendEmail(createParam);     oaNotifyApplicationService.sendOaMsg(createParam);     noteNotifyApplicationService.sendNote(createParam);     wechatNotifyApplicationService.pushMsg(createParam); }
  这里我们只是处理消息通知,如果付款之后还有别的操作呢?例如打印付款记录,创建订单,创建物流的等等。如果我们都写在主体代码中,后面万一撤销功能(如撤消邮件通知,微信通知),这样肯定违反了设计原则中的避开原则。
  如果我们的系统是单体系统,我们可以使用Spring的事件机制: public class PayInfoEvent extends ApplicationEvent {     private PayInfo payInfo;      public PayInfoEvent(Object source, PayInfo payInfo) {         super(source);         this.payInfo = payInfo;     }      public PayInfo getpayInfo() {         return payInfo;     }      public void setpayInfo(PayInfo payInfo) {         this.payInfo = payInfo;     } }@Component @Slf4j public class PayInfoEventPublisher {     @Autowired     public ApplicationEventPublisher applicationEventPublisher;      public void publishPayInfoEvent(PayInfo PayInfo) {         log.info("付款提交后消息推送.....");         if (PayInfo == null) {             log.info("无需推送至sap");             return;         }         applicationEventPublisher.publishEvent(new PayInfoEvent(this, PayInfo));     } }
  我们在提交付款后调用消息推送事件: public void submitPayInfo(PayInfoCmd createParam) {      PayInfoSubmitValidator.validateBeforeSubmit(createParam);     updatePayInfo(createParam);     PayInfo PayInfo = BeanUtils.copy(createParam, PayInfo.class);     PayInfoEventPublisher.publishPayInfoEvent(PayInfo); }
  发送之后,需要监听并订阅:
  PayInfoSapEventReceiver @Component @Slf4j public class PayInfoSapEventReceiver {     @Autowired     public PayInfoService PayInfoService;      @EventListener     public void eventHandler(PayInfoEvent PayInfoEvent) {         PayInfo payInfo = PayInfoEvent.getpayInfo();         PayInfoService.putPayInfo2Sap(payInfo);         log.info("付款推送sap事件已处理完毕,单号【{}】", payInfo.getPayCode());     } }
  PayInfoOaEventReceiver @Component @Slf4j public class PayInfoOaEventReceiver {     @Autowired     public PayInfoService PayInfoService;      @EventListener     public void eventHandler(PayInfoEvent PayInfoEvent) {         PayInfo payInfo = PayInfoEvent.getpayInfo();         PayInfoService.putPayInfo2Oa(payInfo);         log.info("付款推送OA事件已处理完毕,单号【{}】", payInfo.getPayCode());     } }
  依此类推,如果我们的系统是分布式的,例如消息推送,订单推送,物流推送都有各自的系统,这里需要远程调用。我们同样使用 发布-订阅机制 ,例如使用kafka等消息队列中间件在业务系统中发布 领域事件 ,在各自业务系统中消费这些领域事件。
  使用观察者模式需要注意: 消息发送成功,接收消息也成功了,但是处理消息时失败了应该怎么办。
  这个问题留给大家思考。
  当我们要取消某个消息推送时,我们只要将对应类中的@EventListener注释掉即可,不需要修改主体代码。
  上面介绍的策略模式和观察者模式都面向对象语言中的设计模式。
  使用策略模式,我们能在面对不同的需求情况更加灵活的做出不同的策略回应,同时策略模式也提升了代码的扩展性。
  使用观察者模式,我们独立了目标与观察者,降低了两者的依赖性,也是面向对象设计中低耦合的体现。 面向对象思维模式代表——基于领域驱动的设计
  文章上面我们谈到 领域事件 。你或许会好奇:什么是领域事件?
  在这之前我们需要弄明白什么是领域驱动设计。
  领域驱动设计(Domain Driven Design)是一种从系统分析到软件建模的一套方法论。以领域为核心驱动力的设计体系。
  领域驱动设计适合产品化,可持续迭代,业务逻辑足够 复杂的业务 系统,对于系统初期业务逻辑相对比较简单的应用,传统MVC架构更具有优势,可以减少一部分认知成本与开发成本。
  这里我们可以简单认为一个领域即为一个服务。
  我认为基于领域驱动的设计更符合面向对象设计的原则,当我们接触到需求的第一步就需要考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。
  总的来说我们需要先考虑业务语言,而不是数据。
  领域驱动设计将业务语义显性化,更准确的传达业务规则,因此我们可以更清晰的实现代码。
  今天我们简单介绍下在代码中如何运用DDD领域驱动设计模型
  说到DDD,人们首先会讨论充血模型与贫血模型。 贫血模型贫血领域对象
  贫血领域对象(Anemic Domain Object)是指仅用作数据载体,而没有行为和动作的领域对象。
  简单来说,就是只有Getter/Setter方法的实体。既然有贫血领域对象,那也就有充血领域对象。 充血模型充血领域对象
  实体除了Getter/Setter方法,还有描述实体行为和动作的方法
  在充血模型中我们的对象不只有本身的属性,还有相关的行为。来看下面代码:
  上面代码是一个提交进入审批流程的方法,提交后我们需要在后台数据库记录一条提交记录,这个时候需要对数据做一些初始化,例如:初始化审批层级为第一层,初始化节点类型为提交节点,初始化删除标志为未删除。
  如果按照贫血模型来写: public Long submitProcess(ProcessCreateParam createParam) {  ...  process.setDeleted(SystemConstant.ZERO_BYTE);  process.setProcess(SystemConstant.ONE_BYTE);  process.setType(SystemConstant.ONE_BYTE);  ... }
  贫血模型中,我们将对象初始化的具体细节交给了submitProcess()方法来做。然后从面向对象的角度来说,这些是属于对象本身需要做的事情,如果在其他方法中,我们又需要给对象标上非删除标记,初始化层级,设置提交节点。那么在这些方法又要同样的事情。我们为何不将这件事情交给对象本身来做呢?
  在充血模型中我们可以这样写:
  需要时我们直接通过对象调用即可: public Long submitProcess(ProcessCreateParam createParam) {                 ...         Process process = BeanUtils.copy(createParam, Process.class);         process.unDeleted();         //初始化层级:1         process.initLevel();         //初始化节点类型:提交节点         process.submitType();         ...        }
  回到文章开头所说的面向对象设计,充血模型无疑是对面向对象一个很好的诠释。
  关于DDD领域驱动设计,推荐书籍: 《领域驱动设计:软件核心复杂性应对之道》
  《实现领域驱动设计》为什么我们在使用贫血模型
  看了上面的代码,我们可能会疑问: 我使用贫血模型开发挺好的啊?为什么还要使用充血模型?也没看出什么不一样啊?
  传统开发模式的贫血模型,将数据与业务彻底隔离。对象通过Getter/Setter方法修改属性,这样对象属性可能会被随意修改,从而违反了面向对象中的封装特性。
  其实这样的编程方式是传统的面向过程思维编程方式,面向过程的思维方式是符合我们人类的大脑思维逻辑的,不需要太多的设计模式和过多的设计思考。其实我们在开发中存在这样的思维惯性:怎么方便怎么来,代码编写很简单,别人也容易接受。
  因此我总结为什么人们更愿意使用贫血模型呢: 充血模型相对贫血模型存在一定的设计难度,你需要多花时间思考哪些是对象本身的行为
  面向过程的编程思想根深蒂固,很难改变
  对代码没有太大负责态度,认为怎么简单怎么来我们需要使用充血模型么
  DDD领域驱动设计是应对复杂的软件设计而产出的一种思维模式,遵循面向对象的设计思想。在复杂的系统中,我们使用贫血模型(面向过程思维)开发,那最后的结果是 点连成线,线交织成网,密密麻麻不可维护
  然而我们大部分负责的系统并不复杂,我的建议是: 朝充血模型思维方式靠齐我的思考
  如果你还在抱怨自己的工作只是 简单,重复,机械的增删改查 。那么建议你多做一些的思考: 1.我的代码是不是面向对象的代码 2.我的代码设计是否遵循 高内聚,低耦合的设计标准 3.我的代码是否遵循设计原则,如单一职责原则,开闭原则等 4. ...
  从细节处提升自己。

乡约十一,来河南这些特色村落打卡吧大河报豫视频记者张超王永璐十一假期日益临近,来盘盘去哪儿玩?携程发布的2022年国庆预测报告显示,从中秋假期开始,国庆旅游进入稳定增长阶段,日均订单增长速度超过30。郑州近期预订国1952年主席视察河南,遇到失联20年的爱将,当即问道你去哪了?1952年10月份,毛主席奔赴河南视察水利情况。在这里,他看到了一个非常熟悉的面孔,毛主席迎着众人诧异的目光,走向了对方,他拉着那人的手,惊喜地说道好久不见啊,这些年你都去哪啦?过河南信阳富豪陈登志发家史,21岁赤手空拳闯深圳,如今身价超10亿对于广大创业者来说,虽然没有真正意义上的捷径可走,但从那些成功人士的创业故事中,去探寻那些值得我们学习的闪光点,不失为一种可以有效减少创业道路上遇到坎坷的途径。我们在研究了数百位亿损失十几万!60多米高山崖直接跳下,河南辉县百只山羊接连跳崖坠亡9月24日,河南大河报报道了一个令人震惊的新闻,河南新乡辉县市回龙附近的挂壁公路上,近百只山羊直接从悬崖上跳下,跌落高约60多米两层壁挂公路,第三层公路的内侧,现场鲜血淋漓,惨不忍华为mate50评测,值不值得买首先是外观变化,pro版本变成了刘海屏,苹果pro版本变成了挖孔屏,双向奔赴了属于是捂脸普通版圆点挖空加玻璃背板,这个背板非常容易碎,如果入手这个版本要套好保护壳,昆仑玻璃版,这个大话西游2玩家爆料缥缈郎君疑似回归,将继续书写传奇一款游戏,一生朋友,大家好,我是漠颜无情,提到人族的代表人物,你会想到谁?是左左右还是落雨听禅呢?相信大家心中的选择都会不一样,不过有一位人族的代表玩家不得不提,那便是缥缈郎君,这超级城市之武汉2021年各区的GDP排名,东湖高新区领跑本期属于系列文章超级城市万亿俱乐部城市观察的第9篇武汉篇。24大万亿俱乐部城市,值得关注!往期系列文章,欢迎朋友们阅读和留言超级城市之北京各区GDP排名,海淀领跑,前十均过千亿超级面粉里加入1把黑芝麻,没想到这么好吃,不煎不炸也不烙,真过瘾面粉里加入一把黑芝麻,没想到出锅这么好吃,不用发面,不用油炸,上桌孩子抢着吃。先在大碗中加入400克面粉,加入1勺的食盐,增加面粉的筋性,再加入80克的黑芝麻,先搅拌均匀,平常我们荣耀80Pro快马加鞭,16G512G备货很足,状态很好对于手机外观设计你现在最喜欢哪种方式?曲面屏还是直屏?很多喜欢玩游戏的朋友对直屏更青睐,因为在他们眼里直屏带来的游戏触控体验会更好,而对于那些追求时尚的用户来说,现在换机必须是曲面坐标中国云巅机场为世界屋脊插上腾飞翅膀小新说回首我们这十年,中央企业打造的一系列国之重器和超级工程,从不同维度镌刻下国家发展的新坐标。9月12日,我们这十年坐标中国网上主题宣传正式启动,以不断刷新的中国跨度中国精度中国专家北京试行存量房交易连环单将提升市场交易效率中新经纬9月25日电(李惠聪)据北京市住建委网站23日消息,为提高二手住房交易效率,支持购房家庭合理住房需求,北京市住建委和市规自委印发了关于试行存量房交易连环单业务并行办理的通知
多款22年发售的旗舰机售价大幅度下调,性能却依旧领先,不容错过如果您喜欢,可以点击上面的关注二字。后续会为您提供更多有价值的内容。今天分享多款22年发售的旗舰机售价大幅度下调,性能却依旧领先,不容错过第一款一加AcePro参考价格2677元(女人若不想被喊黄脸婆,春天多吃养元5宝,气血双补头条创作挑战赛春天的气候特点昼夜温差大,春燥特别明显。尤其是中老年女性,要好好把握住春季这个养元黄金期。女人随着年龄的增长,生活中各种压力,导致免疫力下降,气色差,这个时候就要加强全新换代雷凌上市,广汽丰田刷新智能化电动化新未来智能化电动化是行业趋势,正在深刻改变汽车的消费观念消费心理使用习惯。适应这一深刻变化,强化智能和电动路线,化解使用中的痛点和难点,稳定车市,需要头部车企们发挥主动性,给出解决方案。20万请明星带货,3个月只卖出278元不可思议,明星主播带货三个月销售额仅278元。一顿操作猛如虎,一看结果250。请明星主播带货,商家惨亏。3月18日,20万请曹姓明星直播带货3个月成交278元登上微博话题热搜榜,引蒙古公然与中国毁约后,中国全面解禁澳煤,蒙煤或将退出中国?蒙古单方面撕毁和中国的直销协议,于是中国将会恢复对澳洲煤炭的购买,寻求自己的老朋友这也就使得蒙古需要承担自己撕毁合约的不良后果,可能会彻底退出中国的煤矿市场。蒙古国一失足成千古恨自伊斯兰文化中最神圣的时刻斋月之夜穆斯林正在迎接神圣的斋月伊斯兰文化中一年中最神圣的时刻。在世界范围内,斋月是禁食和祈祷的一个月。几个世纪以来,斋月也有一套独特的传统,反映了伊斯兰信徒们团结的精神。从庆祝开斋的隆隆聚焦太原两会深化文旅融合建设国际知名文化旅游目的地文化是城市的灵魂,是太原的比较优势,要以文兴城以文塑城,积极创建国家全域旅游示范区,全面提升城市软实力。3月21日,太原市十五届人大三次会议举行第一次全体会议,太原市委副书记市长张昆明滇池度假区滇池文化大使进乡村活动举行探寻文化赋能美丽乡村建设新路径活动合影3月21日,昆明滇池国家旅游度假区第二届点亮滇池圆梦春城文创大赛线下活动滇池文化大使进乡村活动举行,文化名人网络大V等30余人走进度假区杜曲村晋宁区梁王村,通过座谈交流走访23个!山东公布第二批省级旅游休闲街区来源大众报业大众日报客户端3月21日,记者从山东省文化和旅游厅获悉,根据山东省文化和旅游厅山东省发展改革委关于开展第二批省级旅游休闲街区认定工作的通知(鲁文旅资源202228号)要佛山名副其实成为地级城市建设水平倒数排名城市佛山名副其实成为地级城市建设水平倒数排名城市佛山是广东省第三大城市,以富裕文化旅游和商贸四大特色而闻名。名副其实这个词汇,既可以用于褒义,也可以用于贬义。但是在佛山的建设水平方面,这所大学樱花林迎来盛花期漫天粉色花海令人陶醉3月23日,复旦大学邯郸校区光华楼草坪上的樱花林迎来盛花期,吸引了诸多复旦师生驻足赏樱,樱花掩映的校园风景如画。两名女生在樱花树下欣赏拍摄的照片。樱花树下的休闲椅成为休闲拍照的好位