@@ -464,6 +464,185 @@ memo:2025 年 8 月 12 日修改至此,今天有球友在VIP 群里讲,他
464464
465465后面想添加新的 AI 服务,只需要增加一个新的策略类,不需要修改原有代码,这样就提高了代码的可扩展性。
466466
467+ ### 你想做一个导出的数据导出的功能,然后他可能导出的格式有Excel、有JSON,可能有XML,然后如果你用面向对象这些方法去做,会怎么去设计这个实现?
468+
469+ 我会使用策略模式+工厂模式来实现。
470+
471+ 首先,我会定义一个导出接口 ` DataExporter ` ,该接口包含一个导出方法 ` export(data) ` ,用于导出数据。
472+
473+ ``` java
474+ public interface DataExporter {
475+ void export (List<Object > data , OutputStream outputStream ) throws Exception ;
476+ String getContentType ();
477+ String getFileExtension ();
478+ }
479+ ```
480+
481+ 然后,我会为每种导出格式(Excel、JSON、XML)创建具体的策略类,这些类实现了 ` ExportStrategy ` 接口,并提供各自的导出逻辑。
482+
483+ ``` java
484+ public class ExcelExporter implements DataExporter {
485+ @Override
486+ public void export (List<Object > data , OutputStream outputStream ) throws Exception {
487+ Workbook workbook = new XSSFWorkbook ();
488+ Sheet sheet = workbook. createSheet(" Data" );
489+
490+ // 写入表头
491+ if (! data. isEmpty()) {
492+ Object firstRow = data. get(0 );
493+ Row headerRow = sheet. createRow(0 );
494+ Field [] fields = firstRow. getClass(). getDeclaredFields();
495+ for (int i = 0 ; i < fields. length; i++ ) {
496+ headerRow. createCell(i). setCellValue(fields[i]. getName());
497+ }
498+
499+ // 写入数据
500+ for (int i = 0 ; i < data. size(); i++ ) {
501+ Row dataRow = sheet. createRow(i + 1 );
502+ Object obj = data. get(i);
503+ for (int j = 0 ; j < fields. length; j++ ) {
504+ fields[j]. setAccessible(true );
505+ Object value = fields[j]. get(obj);
506+ dataRow. createCell(j). setCellValue(value != null ? value. toString() : " " );
507+ }
508+ }
509+ }
510+
511+ workbook. write(outputStream);
512+ workbook. close();
513+ }
514+
515+ @Override
516+ public String getContentType () {
517+ return " application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ;
518+ }
519+
520+ @Override
521+ public String getFileExtension () {
522+ return " xlsx" ;
523+ }
524+ }
525+
526+ public class JsonExporter implements DataExporter {
527+ private ObjectMapper objectMapper = new ObjectMapper ();
528+
529+ @Override
530+ public void export (List<Object > data , OutputStream outputStream ) throws Exception {
531+ objectMapper. writeValue(outputStream, data);
532+ }
533+
534+ @Override
535+ public String getContentType () {
536+ return " application/json" ;
537+ }
538+
539+ @Override
540+ public String getFileExtension () {
541+ return " json" ;
542+ }
543+ }
544+
545+ public class XmlExporter implements DataExporter {
546+ @Override
547+ public void export (List<Object > data , OutputStream outputStream ) throws Exception {
548+ DocumentBuilderFactory factory = DocumentBuilderFactory . newInstance();
549+ DocumentBuilder builder = factory. newDocumentBuilder();
550+ Document document = builder. newDocument();
551+
552+ Element root = document. createElement(" data" );
553+ document. appendChild(root);
554+
555+ for (Object obj : data) {
556+ Element item = document. createElement(" item" );
557+ Field [] fields = obj. getClass(). getDeclaredFields();
558+ for (Field field : fields) {
559+ field. setAccessible(true );
560+ Element element = document. createElement(field. getName());
561+ Object value = field. get(obj);
562+ element. setTextContent(value != null ? value. toString() : " " );
563+ item. appendChild(element);
564+ }
565+ root. appendChild(item);
566+ }
567+
568+ TransformerFactory transformerFactory = TransformerFactory . newInstance();
569+ Transformer transformer = transformerFactory. newTransformer();
570+ transformer. setOutputProperty(OutputKeys . INDENT , " yes" );
571+ DOMSource source = new DOMSource (document);
572+ StreamResult result = new StreamResult (outputStream);
573+ transformer. transform(source, result);
574+ }
575+
576+ @Override
577+ public String getContentType () {
578+ return " application/xml" ;
579+ }
580+
581+ @Override
582+ public String getFileExtension () {
583+ return " xml" ;
584+ }
585+ }
586+ ```
587+
588+ 接下来,我会创建一个工厂类 ` DataExporterFactory ` ,用于根据导出格式创建相应的导出策略实例。
589+
590+ ``` java
591+ public class DataExporterFactory {
592+ private static final Map<String , DataExporter > exporters = new HashMap<> ();
593+
594+ static {
595+ exporters. put(" excel" , new ExcelExporter ());
596+ exporters. put(" json" , new JsonExporter ());
597+ exporters. put(" xml" , new XmlExporter ());
598+ }
599+
600+ public static DataExporter getExporter (String format ) {
601+ DataExporter exporter = exporters. get(format. toLowerCase());
602+ if (exporter == null ) {
603+ throw new IllegalArgumentException (" 不支持的导出格式: " + format);
604+ }
605+ return exporter;
606+ }
607+
608+ public static Set<String > getSupportedFormats () {
609+ return exporters. keySet();
610+ }
611+ }
612+ ```
613+
614+ 最后,我会定义导出服务的上下文类,我根据请求参数选择合适的导出策略,并调用其导出方法。
615+
616+ ``` java
617+ @Service
618+ public class DataExportService {
619+
620+ public void exportData (List<Object > data , String format , OutputStream outputStream )
621+ throws Exception {
622+ DataExporter exporter = DataExporterFactory . getExporter(format);
623+ exporter. export(data, outputStream);
624+ }
625+
626+ public ResponseEntity<byte[]> exportDataAsResponse (List<Object > data , String format )
627+ throws Exception {
628+ DataExporter exporter = DataExporterFactory . getExporter(format);
629+
630+ ByteArrayOutputStream baos = new ByteArrayOutputStream ();
631+ exporter. export(data, baos);
632+
633+ HttpHeaders headers = new HttpHeaders ();
634+ headers. setContentType(MediaType . parseMediaType(exporter. getContentType()));
635+ headers. setContentDispositionFormData(" attachment" ,
636+ " export_" + System . currentTimeMillis() + " ." + exporter. getFileExtension());
637+
638+ return ResponseEntity . ok()
639+ .headers(headers)
640+ .body(baos. toByteArray());
641+ }
642+ }
643+ ```
644+
645+
467646> 1 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的字节跳动面经同学 1 Java 后端技术一面面试原题:了解哪些设计模式?
468647> 2 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的奇安信面经同学 1 Java 技术一面面试原题:你真正使用过哪些设计模式?
469648> 3 . [ Java 面试指南(付费)] ( https://javabetter.cn/zhishixingqiu/mianshi.html ) 收录的农业银行面经同学 7 Java 后端面试原题:介绍你熟悉的设计模式
0 commit comments